summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2015-10-21 16:57:52 -0500
committerMichael Merickel <michael@merickel.org>2015-10-21 16:57:52 -0500
commitadb9377a963d7fdc7b7bf616740fb5dd2e40b2bf (patch)
tree1038d03a21148607c79f740f0b32b147929251bf
parent452fdbef94bb29560497ec8a9ccbc3b9c2ecd2dd (diff)
parentd4221720b8409eafb65b301562be327af0196c7e (diff)
downloadpyramid-adb9377a963d7fdc7b7bf616740fb5dd2e40b2bf.tar.gz
pyramid-adb9377a963d7fdc7b7bf616740fb5dd2e40b2bf.tar.bz2
pyramid-adb9377a963d7fdc7b7bf616740fb5dd2e40b2bf.zip
Merge branch 'master' into feature/alchemy-scaffold-update
-rw-r--r--.travis.yml34
-rw-r--r--CHANGES.txt29
-rw-r--r--CONTRIBUTORS.txt6
-rw-r--r--HACKING.txt2
-rw-r--r--README.rst4
-rw-r--r--contributing.md87
-rw-r--r--docs/_static/directory_structure_generic.pngbin11845 -> 0 bytes
-rw-r--r--docs/_static/directory_structure_initial.pngbin7752 -> 0 bytes
-rw-r--r--docs/_static/directory_structure_pyramid.pngbin16746 -> 0 bytes
-rw-r--r--docs/api/request.rst2
-rw-r--r--docs/conf.py41
-rw-r--r--docs/conventions.rst12
-rw-r--r--docs/copyright.rst6
-rw-r--r--docs/narr/assets.rst511
-rw-r--r--docs/narr/commandline.rst503
-rw-r--r--docs/narr/configuration.rst92
-rw-r--r--docs/narr/environment.rst344
-rw-r--r--docs/narr/events.rst156
-rw-r--r--docs/narr/firstapp.rst191
-rw-r--r--docs/narr/i18n.rst640
-rw-r--r--docs/narr/install.rst39
-rw-r--r--docs/narr/introduction.rst716
-rw-r--r--docs/narr/logging.rst310
-rw-r--r--docs/narr/paste.rst96
-rw-r--r--docs/narr/project-debug.pngbin106878 -> 202464 bytes
-rw-r--r--docs/narr/project-show-toolbar.pngbin0 -> 11050 bytes
-rw-r--r--docs/narr/project.pngbin91662 -> 133242 bytes
-rw-r--r--docs/narr/project.rst703
-rw-r--r--docs/narr/renderers.rst321
-rw-r--r--docs/narr/router.rst81
-rw-r--r--docs/narr/scaffolding.rst5
-rw-r--r--docs/narr/sessions.rst245
-rw-r--r--docs/narr/startup.rst108
-rw-r--r--docs/narr/templates.rst309
-rw-r--r--docs/narr/urldispatch.rst518
-rw-r--r--docs/narr/vhosting.rst117
-rw-r--r--docs/narr/viewconfig.rst574
-rw-r--r--docs/narr/views.rst296
-rw-r--r--docs/narr/webob.rst451
-rw-r--r--docs/quick_tour.rst153
-rw-r--r--docs/quick_tutorial/authentication.rst5
-rw-r--r--docs/quick_tutorial/authentication/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/authentication/tutorial/login.pt4
-rw-r--r--docs/quick_tutorial/authorization.rst9
-rw-r--r--docs/quick_tutorial/authorization/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/authorization/tutorial/login.pt4
-rw-r--r--docs/quick_tutorial/databases.rst44
-rw-r--r--docs/quick_tutorial/databases/development.ini36
-rw-r--r--docs/quick_tutorial/databases/tutorial/tests.py6
-rw-r--r--docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt6
-rw-r--r--docs/quick_tutorial/debugtoolbar.rst32
-rw-r--r--docs/quick_tutorial/forms.rst6
-rw-r--r--docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt2
-rw-r--r--docs/quick_tutorial/functional_testing.rst2
-rw-r--r--docs/quick_tutorial/ini.rst8
-rw-r--r--docs/quick_tutorial/jinja2.rst2
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/home.jinja24
-rw-r--r--docs/quick_tutorial/json/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/logging.rst23
-rw-r--r--docs/quick_tutorial/logging/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes.rst21
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/delete.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/edit.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/hello.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/package.rst4
-rw-r--r--docs/quick_tutorial/request_response.rst3
-rw-r--r--docs/quick_tutorial/requirements.rst36
-rw-r--r--docs/quick_tutorial/routing.rst2
-rw-r--r--docs/quick_tutorial/routing/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/sessions.rst2
-rw-r--r--docs/quick_tutorial/sessions/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/static_assets.rst2
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/templating.rst2
-rw-r--r--docs/quick_tutorial/templating/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/unit_testing.rst2
-rw-r--r--docs/quick_tutorial/view_classes.rst4
-rw-r--r--docs/quick_tutorial/views.rst2
-rw-r--r--docs/tutorials/wiki/authorization.rst372
-rw-r--r--docs/tutorials/wiki/basiclayout.rst49
-rw-r--r--docs/tutorials/wiki/definingmodels.rst25
-rw-r--r--docs/tutorials/wiki/definingviews.rst298
-rw-r--r--docs/tutorials/wiki/design.rst37
-rw-r--r--docs/tutorials/wiki/distributing.rst34
-rw-r--r--docs/tutorials/wiki/index.rst8
-rw-r--r--docs/tutorials/wiki/installation.rst266
-rw-r--r--docs/tutorials/wiki/src/authorization/setup.py4
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt120
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt118
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt121
-rw-r--r--docs/tutorials/wiki/src/basiclayout/setup.py4
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki/src/models/setup.py4
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki/src/tests/setup.py4
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt120
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/login.pt118
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/view.pt121
-rw-r--r--docs/tutorials/wiki/src/views/setup.py4
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/edit.pt115
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt130
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/view.pt120
-rw-r--r--docs/tutorials/wiki/tests.rst44
-rw-r--r--docs/tutorials/wiki2/authorization.rst409
-rw-r--r--docs/tutorials/wiki2/background.rst8
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst22
-rw-r--r--docs/tutorials/wiki2/definingmodels.rst115
-rw-r--r--docs/tutorials/wiki2/definingviews.rst227
-rw-r--r--docs/tutorials/wiki2/design.rst25
-rw-r--r--docs/tutorials/wiki2/distributing.rst28
-rw-r--r--docs/tutorials/wiki2/index.rst3
-rw-r--r--docs/tutorials/wiki2/installation.rst166
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt122
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt122
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt130
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt125
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/models.py3
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt120
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt123
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/ie6.css8
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pylons.css372
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pyramid.pngbin33055 -> 12901 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/theme.css154
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/theme.min.css1
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt111
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt127
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/view.pt114
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views.py1
-rw-r--r--docs/tutorials/wiki2/tests.rst47
-rw-r--r--pyramid/config/views.py16
-rw-r--r--pyramid/httpexceptions.py27
-rw-r--r--pyramid/interfaces.py2
-rw-r--r--pyramid/renderers.py4
-rw-r--r--pyramid/scaffolds/__init__.py4
-rw-r--r--pyramid/scaffolds/alchemy/+package+/static/theme.css8
-rw-r--r--pyramid/scaffolds/alchemy/+package+/static/theme.min.css2
-rw-r--r--pyramid/scaffolds/alchemy/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/starter/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/zodb/development.ini_tmpl2
-rw-r--r--pyramid/scripts/pcreate.py96
-rw-r--r--pyramid/scripts/pserve.py17
-rw-r--r--pyramid/scripts/pshell.py88
-rw-r--r--pyramid/tests/test_httpexceptions.py17
-rw-r--r--pyramid/tests/test_renderers.py12
-rw-r--r--pyramid/tests/test_scaffolds/test_init.py5
-rw-r--r--pyramid/tests/test_scripts/dummy.py23
-rw-r--r--pyramid/tests/test_scripts/test_pcreate.py46
-rw-r--r--pyramid/tests/test_scripts/test_pserve.py6
-rw-r--r--pyramid/tests/test_scripts/test_pshell.py192
-rw-r--r--pyramid/tests/test_session.py13
-rw-r--r--pyramid/tests/test_view.py14
-rw-r--r--pyramid/util.py9
-rw-r--r--pyramid/view.py14
-rw-r--r--rtd.txt6
-rw-r--r--setup.py9
-rw-r--r--tox.ini9
277 files changed, 8811 insertions, 10480 deletions
diff --git a/.travis.yml b/.travis.yml
index ba077d342..79d9fa09d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,18 +2,28 @@
language: python
sudo: false
-env:
- - TOXENV=py26
- - TOXENV=py27
- - TOXENV=py32
- - TOXENV=py33
- - TOXENV=py34
- - TOXENV=pypy
- - TOXENV=pypy3
- - TOXENV=py2-docs
- - TOXENV=py3-docs
- - TOXENV=py2-cover,py3-cover,coverage
- - TOXENV=pep8
+matrix:
+ include:
+ - python: 2.6
+ env: TOXENV=py26
+ - python: 2.7
+ env: TOXENV=py27
+ - python: 3.2
+ env: TOXENV=py32
+ - python: 3.3
+ env: TOXENV=py33
+ - python: 3.4
+ env: TOXENV=py34
+ - python: 3.5
+ env: TOXENV=py35
+ - python: pypy
+ env: TOXENV=pypy
+ - python: pypy3
+ env: TOXENV=pypy3
+ - python: 3.5
+ env: TOXENV=py2-cover,py3-cover,coverage
+ - python: 3.5
+ env: TOXENV=pep8
install:
- travis_retry pip install tox
diff --git a/CHANGES.txt b/CHANGES.txt
index 87e9f1f3a..4396c4356 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,12 @@
Features
--------
+- pcreate will now ask for confirmation if invoked with
+ an argument for a project name that already exists or
+ is importable in the current environment.
+ See https://github.com/Pylons/pyramid/issues/1357 and
+ https://github.com/Pylons/pyramid/pull/1837
+
- Make it possible to subclass ``pyramid.request.Request`` and also use
``pyramid.request.Request.add_request.method``. See
https://github.com/Pylons/pyramid/issues/1529
@@ -128,6 +134,13 @@ Features
that as the response class instead of the default ``HTTPFound``. See
https://github.com/Pylons/pyramid/pull/1610
+- Additional shells for ``pshell`` can now be registered as entrypoints. See
+ https://github.com/Pylons/pyramid/pull/1891
+
+- The variables injected into ``pshell`` are now displayed with their
+ docstrings instead of the default ``str(obj)`` when possible.
+ See https://github.com/Pylons/pyramid/pull/1929
+
Bug Fixes
---------
@@ -195,9 +208,25 @@ Bug Fixes
default to an iterable instead of ``None``. It may be checked for a length
of 0. This was the behavior in 1.5.
+- ``pyramid.httpexceptions.HTTPException`` now defaults to
+ ``520 Unknown Error`` instead of ``None None`` to conform with changes in
+ WebOb 1.5.
+ See https://github.com/Pylons/pyramid/pull/1865
+
Deprecations
------------
+- The ``pserve`` command's daemonization features have been deprecated. This
+ includes the ``[start,stop,restart,status]`` subcommands as well as the
+ ``--daemon``, ``--stop-server``, ``--pid-file``, and ``--status`` flags.
+
+ Please use a real process manager in the future instead of relying on the
+ ``pserve`` to daemonize itself. Many options exist including your Operating
+ System's services such as Systemd or Upstart, as well as Python-based
+ solutions like Circus and Supervisor.
+
+ See https://github.com/Pylons/pyramid/pull/1641
+
- Renamed the ``principal`` argument to ``pyramid.security.remember()`` to
``userid`` in order to clarify its intended purpose.
See https://github.com/Pylons/pyramid/pull/1399
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 295e0ffe9..5191199b7 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -248,3 +248,9 @@ Contributors
- Donald Stufft, 2015/03/15
- Randy Topliffe, 2015/04/14
+
+- Karen Dalton, 2015/06/01
+
+- Igor Stroh, 2015/06/10
+
+- Jesse Dhillon, 2015/10/07
diff --git a/HACKING.txt b/HACKING.txt
index b82041c71..d0f9a769e 100644
--- a/HACKING.txt
+++ b/HACKING.txt
@@ -189,7 +189,7 @@ Running Tests
Run the tests like so::
$ $VENV/bin/easy_install pytest
- $ py.test --strict pyramid/
+ $ $VENV/bin/py.test --strict pyramid/
- Functional tests related to the "scaffolds" (starter, zodb, alchemy) which
create a virtualenv, install the scaffold package and its dependencies, start
diff --git a/README.rst b/README.rst
index 6de42ea40..e99133441 100644
--- a/README.rst
+++ b/README.rst
@@ -12,6 +12,10 @@ Pyramid
:target: http://docs.pylonsproject.org/projects/pyramid/en/latest/
:alt: Latest Documentation Status
+.. image:: https://img.shields.io/badge/irc-freenode-blue.svg
+ :target: https://webchat.freenode.net/?channels=pyramid
+ :alt: IRC Freenode
+
Pyramid is a small, fast, down-to-earth, open source Python web framework.
It makes real-world web application development and
deployment more fun, more predictable, and more productive.
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 000000000..6ee28703c
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,87 @@
+Contributing
+============
+
+All projects under the Pylons Projects, including this one, follow the
+guidelines established at [How to
+Contribute](http://www.pylonsproject.org/community/how-to-contribute).
+
+You can contribute to this project in several ways.
+
+* [File an Issue on GitHub](https://github.com/Pylons/pyramid/issues)
+* Fork this project and create a branch with your suggested change. When ready,
+ submit a pull request for consideration. [GitHub
+ Flow](https://guides.github.com/introduction/flow/index.html) describes the
+ workflow process and why it's a good practice. When submitting a pull
+ request, sign
+ [CONTRIBUTORS.txt](https://github.com/Pylons/pyramid/blob/master/CONTRIBUTORS.
+txt)
+ if you have not yet done so.
+* Join the IRC channel #pyramid on irc.freenode.net.
+
+Prerequisites
+-------------
+
+Follow the instructions in HACKING.txt for your version or branch located in
+the [root of the Pyramid repository](https://github.com/Pylons/pyramid/) to
+install Pyramid and the tools needed to run its tests and build its
+documentation.
+
+Building documentation for a Pylons Project project
+---------------------------------------------------
+
+*Note:* These instructions might not work for Windows users. Suggestions to
+improve the process for Windows users are welcome by submitting an issue or a
+pull request. Windows users may find it helpful to follow the guide [Installing
+Pyramid on a Windows
+System](http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/install.html#installing-pyramid-on-a-windows-system).
+
+1. Fork the repo on GitHub by clicking the [Fork] button.
+2. Clone your fork into a workspace on your local machine.
+
+ git@github.com:<username>/pyramid.git
+
+3. Add a git remote "upstream" for the cloned fork.
+
+ git remote add upstream git@github.com:Pylons/pyramid.git
+
+4. Set an environment variable as instructed in the
+ [prerequisites](https://github.com/Pylons/pyramid/blob/master/HACKING.txt#L55-L58).
+
+ # Mac and Linux
+ $ export VENV=~/hack-on-pyramid/env
+
+ # Windows
+ set VENV=c:\hack-on-pyramid\env
+
+5. Try to build the docs in your workspace.
+
+ # Mac and Linux
+ $ make clean html SPHINXBUILD=$VENV/bin/sphinx-build
+
+ # Windows
+ c:\> make clean html SPHINXBUILD=%VENV%\bin\sphinx-build
+
+ If successful, then you can make changes to the documentation. You can
+ load the built documentation in the `/_build/html/` directory in a web
+ browser.
+
+6. From this point forward, follow the typical git workflow. Start by pulling
+ from the upstream to get the most current changes.
+
+ git pull upstream master
+
+7. Make a branch, make changes to the docs, and rebuild them as indicated in
+ step 5. To speed up the build process, you can omit `clean` from the above
+ command to rebuild only those pages that depend on the files you have
+ changed.
+
+8. Once you are satisfied with your changes and the documentation builds
+ successfully without errors or warnings, then git commit and push them to
+ your "origin" repository on GitHub.
+
+ git commit -m "commit message"
+ git push -u origin --all # first time only, subsequent can be just 'git push'.
+
+9. Create a [pull request](https://help.github.com/articles/using-pull-requests/).
+
+10. Repeat the process starting from Step 6.
diff --git a/docs/_static/directory_structure_generic.png b/docs/_static/directory_structure_generic.png
deleted file mode 100644
index c6d1a5b03..000000000
--- a/docs/_static/directory_structure_generic.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/directory_structure_initial.png b/docs/_static/directory_structure_initial.png
deleted file mode 100644
index 000f1bb27..000000000
--- a/docs/_static/directory_structure_initial.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/directory_structure_pyramid.png b/docs/_static/directory_structure_pyramid.png
deleted file mode 100644
index 74edd6533..000000000
--- a/docs/_static/directory_structure_pyramid.png
+++ /dev/null
Binary files differ
diff --git a/docs/api/request.rst b/docs/api/request.rst
index b325ad076..105ffb5a7 100644
--- a/docs/api/request.rst
+++ b/docs/api/request.rst
@@ -11,7 +11,7 @@
:exclude-members: add_response_callback, add_finished_callback,
route_url, route_path, current_route_url,
current_route_path, static_url, static_path,
- model_url, resource_url, set_property,
+ model_url, resource_url, resource_path, set_property,
effective_principals, authenticated_userid,
unauthenticated_userid, has_permission
diff --git a/docs/conf.py b/docs/conf.py
index 11e42c5f3..8a9bac6ed 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -58,37 +58,24 @@ extensions = [
# Looks for objects in external projects
intersphinx_mapping = {
- 'tutorials': ('http://docs.pylonsproject.org/projects/pyramid-tutorials/en/latest/', None),
+ 'colander': ( 'http://docs.pylonsproject.org/projects/colander/en/latest', None),
'cookbook': ('http://docs.pylonsproject.org/projects/pyramid-cookbook/en/latest/', None),
+ 'deform': ('http://docs.pylonsproject.org/projects/deform/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://repozewho.readthedocs.org/en/latest', None),
+ 'pylonswebframework': ('http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/', None),
'python': ('http://docs.python.org', None),
'python3': ('http://docs.python.org/3', None),
- 'tstring':
- ('http://docs.pylonsproject.org/projects/translationstring/en/latest',
- None),
- 'venusian':
- ('http://docs.pylonsproject.org/projects/venusian/en/latest', None),
- 'toolbar':
- ('http://docs.pylonsproject.org/projects/pyramid-debugtoolbar/en/latest',
- None),
- 'zcml':
- ('http://docs.pylonsproject.org/projects/pyramid-zcml/en/latest',
- None),
+ 'sqla': ('http://docs.sqlalchemy.org/en/latest', None),
+ 'tm': ('http://docs.pylonsproject.org/projects/pyramid_tm/en/latest/', None),
+ 'toolbar': ('http://docs.pylonsproject.org/projects/pyramid-debugtoolbar/en/latest', None),
+ 'tstring': ('http://docs.pylonsproject.org/projects/translationstring/en/latest', None),
+ 'tutorials': ('http://docs.pylonsproject.org/projects/pyramid-tutorials/en/latest/', None),
+ 'venusian': ('http://docs.pylonsproject.org/projects/venusian/en/latest', None),
+ 'webob': ('http://docs.webob.org/en/latest', None),
+ 'webtest': ('http://webtest.pythonpaste.org/en/latest', None),
+ 'who': ('http://repozewho.readthedocs.org/en/latest', None),
+ 'zcml': ('http://docs.pylonsproject.org/projects/pyramid-zcml/en/latest', None),
+ 'zcomponent': ('http://docs.zope.org/zope.component', None),
}
diff --git a/docs/conventions.rst b/docs/conventions.rst
index 21b506623..a9d2550bf 100644
--- a/docs/conventions.rst
+++ b/docs/conventions.rst
@@ -1,19 +1,19 @@
Typographical Conventions
=========================
-Literals, filenames and function arguments are presented using the
+Literals, filenames, and function arguments are presented using the
following style:
``argument1``
-Warnings, which represent limitations and need-to-know information
+Warnings which represent limitations and need-to-know information
related to a topic or concept are presented in the following style:
.. warning::
This is a warning.
-Notes, which represent additional information related to a topic or
+Notes which represent additional information related to a topic or
concept are presented in the following style:
.. note::
@@ -24,7 +24,7 @@ We present Python method names using the following style:
:meth:`pyramid.config.Configurator.add_view`
-We present Python class names, module names, attributes and global
+We present Python class names, module names, attributes, and global
variables using the following style:
:class:`pyramid.config.Configurator.registry`
@@ -105,10 +105,10 @@ It may look unusual, but it has advantages:
* It allows one to swap out the higher-level package ``foo`` for something
else that provides the similar API. An example would be swapping out
- one Database for another (e.g. graduating from SQLite to PostgreSQL).
+ one database for another (e.g., graduating from SQLite to PostgreSQL).
* Looks more neat in cases where a large number of objects get imported from
that package.
-* Adding/removing imported objects from the package is quicker and results
+* Adding or removing imported objects from the package is quicker and results
in simpler diffs.
diff --git a/docs/copyright.rst b/docs/copyright.rst
index 980335827..3beaee7f7 100644
--- a/docs/copyright.rst
+++ b/docs/copyright.rst
@@ -39,7 +39,7 @@ any trademark or service mark.
Every effort has been made to make this book as complete and as
accurate as possible, but no warranty or fitness is implied. The
-information provided is on as "as-is" basis. The author and the
+information provided is on an "as-is" basis. The author and the
publisher shall have neither liability nor responsibility to any
person or entity with respect to any loss or damages arising from the
information contained in this book. No patent liability is assumed
@@ -89,14 +89,14 @@ Contacting The Publisher
Please send documentation licensing inquiries, translation inquiries,
and other business communications to `Agendaless Consulting
<mailto:webmaster@agendaless.com>`_. Please send software and other
-technical queries to the `Pylons-devel maillist
+technical queries to the `Pylons-devel mailing list
<http://groups.google.com/group/pylons-devel>`_.
HTML Version and Source Code
----------------------------
An HTML version of this book is freely available via
-http://docs.pylonsproject.org
+http://docs.pylonsproject.org/projects/pyramid/en/latest/
The source code for the examples used in this book are available
within the :app:`Pyramid` software distribution, always available
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index d6bc8cbb8..020794062 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -7,8 +7,8 @@
Static Assets
=============
-An :term:`asset` is any file contained within a Python :term:`package` which
-is *not* a Python source code file. For example, each of the following is an
+An :term:`asset` is any file contained within a Python :term:`package` which is
+*not* a Python source code file. For example, each of the following is an
asset:
- a GIF image file contained within a Python package or contained within any
@@ -20,20 +20,20 @@ asset:
- a JavaScript source file contained within a Python package or contained
within any subdirectory of a Python package.
-- A directory within a package that does not have an ``__init__.py``
- in it (if it possessed an ``__init__.py`` it would *be* a package).
+- A directory within a package that does not have an ``__init__.py`` in it (if
+ it possessed an ``__init__.py`` it would *be* a package).
- a :term:`Chameleon` or :term:`Mako` template file contained within a Python
package.
The use of assets is quite common in most web development projects. For
example, when you create a :app:`Pyramid` application using one of the
-available scaffolds, as described in :ref:`creating_a_project`, the
-directory representing the application contains a Python :term:`package`.
-Within that Python package, there are directories full of files which are
-static assets. For example, there's a ``static`` directory which contains
-``.css``, ``.js``, and ``.gif`` files. These asset files are delivered when
-a user visits an application URL.
+available scaffolds, as described in :ref:`creating_a_project`, the directory
+representing the application contains a Python :term:`package`. Within that
+Python package, there are directories full of files which are static assets.
+For example, there's a ``static`` directory which contains ``.css``, ``.js``,
+and ``.gif`` files. These asset files are delivered when a user visits an
+application URL.
.. index::
single: asset specifications
@@ -45,10 +45,10 @@ Understanding Asset Specifications
Let's imagine you've created a :app:`Pyramid` application that uses a
:term:`Chameleon` ZPT template via the
-:func:`pyramid.renderers.render_to_response` API. For example, the
-application might address the asset using the :term:`asset specification`
-``myapp:templates/some_template.pt`` using that API within a ``views.py``
-file inside a ``myapp`` package:
+:func:`pyramid.renderers.render_to_response` API. For example, the application
+might address the asset using the :term:`asset specification`
+``myapp:templates/some_template.pt`` using that API within a ``views.py`` file
+inside a ``myapp`` package:
.. code-block:: python
:linenos:
@@ -66,23 +66,23 @@ two parts:
- The *asset name* (``templates/some_template.pt``), relative to the package
directory.
-The two parts are separated by the colon character.
+The two parts are separated by a colon ``:`` character.
-:app:`Pyramid` uses the Python :term:`pkg_resources` API to resolve the
-package name and asset name to an absolute (operating-system-specific) file
-name. It eventually passes this resolved absolute filesystem path to the
-Chameleon templating engine, which then uses it to load, parse, and execute
-the template file.
+:app:`Pyramid` uses the Python :term:`pkg_resources` API to resolve the package
+name and asset name to an absolute (operating system-specific) file name. It
+eventually passes this resolved absolute filesystem path to the Chameleon
+templating engine, which then uses it to load, parse, and execute the template
+file.
There is a second form of asset specification: a *relative* asset
specification. Instead of using an "absolute" asset specification which
includes the package name, in certain circumstances you can omit the package
name from the specification. For example, you might be able to use
``templates/mytemplate.pt`` instead of ``myapp:templates/some_template.pt``.
-Such asset specifications are usually relative to a "current package." The
+Such asset specifications are usually relative to a "current package". The
"current package" is usually the package which contains the code that *uses*
the asset specification. :app:`Pyramid` APIs which accept relative asset
-specifications typically describe what the asset is relative to in their
+specifications typically describe to what the asset is relative in their
individual documentation.
.. index::
@@ -96,17 +96,17 @@ Serving Static Assets
:app:`Pyramid` makes it possible to serve up static asset files from a
directory on a filesystem to an application user's browser. Use the
-:meth:`pyramid.config.Configurator.add_static_view` to instruct
-:app:`Pyramid` to serve static assets such as JavaScript and CSS files. This
-mechanism makes a directory of static files available at a name relative to
-the application root URL, e.g. ``/static`` or as an external URL.
+:meth:`pyramid.config.Configurator.add_static_view` to instruct :app:`Pyramid`
+to serve static assets, such as JavaScript and CSS files. This mechanism makes
+a directory of static files available at a name relative to the application
+root URL, e.g., ``/static``, or as an external URL.
.. note::
- :meth:`~pyramid.config.Configurator.add_static_view` cannot serve a
- single file, nor can it serve a directory of static files directly
- relative to the root URL of a :app:`Pyramid` application. For these
- features, see :ref:`advanced_static`.
+ :meth:`~pyramid.config.Configurator.add_static_view` cannot serve a single
+ file, nor can it serve a directory of static files directly relative to the
+ root URL of a :app:`Pyramid` application. For these features, see
+ :ref:`advanced_static`.
Here's an example of a use of
:meth:`~pyramid.config.Configurator.add_static_view` that will serve files up
@@ -121,11 +121,11 @@ from the ``/var/www/static`` directory of the computer which runs the
The ``name`` represents a URL *prefix*. In order for files that live in the
``path`` directory to be served, a URL that requests one of them must begin
-with that prefix. In the example above, ``name`` is ``static``, and ``path``
-is ``/var/www/static``. In English, this means that you wish to serve the
-files that live in ``/var/www/static`` as sub-URLs of the ``/static`` URL
-prefix. Therefore, the file ``/var/www/static/foo.css`` will be returned
-when the user visits your application's URL ``/static/foo.css``.
+with that prefix. In the example above, ``name`` is ``static`` and ``path`` is
+``/var/www/static``. In English this means that you wish to serve the files
+that live in ``/var/www/static`` as sub-URLs of the ``/static`` URL prefix.
+Therefore, the file ``/var/www/static/foo.css`` will be returned when the user
+visits your application's URL ``/static/foo.css``.
A static directory named at ``path`` may contain subdirectories recursively,
and any subdirectories may hold files; these will be resolved by the static
@@ -134,16 +134,16 @@ view for each particular type of file is dependent upon its file extension.
By default, all files made available via
:meth:`~pyramid.config.Configurator.add_static_view` are accessible by
-completely anonymous users. Simple authorization can be required, however.
-To protect a set of static files using a permission, in addition to passing
-the required ``name`` and ``path`` arguments, also pass the ``permission``
-keyword argument to :meth:`~pyramid.config.Configurator.add_static_view`.
-The value of the ``permission`` argument represents the :term:`permission`
-that the user must have relative to the current :term:`context` when the
-static view is invoked. A user will be required to possess this permission
-to view any of the files represented by ``path`` of the static view. If your
-static assets must be protected by a more complex authorization scheme,
-see :ref:`advanced_static`.
+completely anonymous users. Simple authorization can be required, however. To
+protect a set of static files using a permission, in addition to passing the
+required ``name`` and ``path`` arguments, also pass the ``permission`` keyword
+argument to :meth:`~pyramid.config.Configurator.add_static_view`. The value of
+the ``permission`` argument represents the :term:`permission` that the user
+must have relative to the current :term:`context` when the static view is
+invoked. A user will be required to possess this permission to view any of the
+files represented by ``path`` of the static view. If your static assets must
+be protected by a more complex authorization scheme, see
+:ref:`advanced_static`.
Here's another example that uses an :term:`asset specification` instead of an
absolute path as the ``path`` argument. To convince
@@ -163,31 +163,31 @@ may be a fully qualified :term:`asset specification` or an *absolute path*.
Instead of representing a URL prefix, the ``name`` argument of a call to
:meth:`~pyramid.config.Configurator.add_static_view` can alternately be a
-*URL*. Each of examples we've seen so far have shown usage of the ``name``
-argument as a URL prefix. However, when ``name`` is a *URL*, static assets
-can be served from an external webserver. In this mode, the ``name`` is used
-as the URL prefix when generating a URL using
+*URL*. Each of the examples we've seen so far have shown usage of the ``name``
+argument as a URL prefix. However, when ``name`` is a *URL*, static assets can
+be served from an external webserver. In this mode, the ``name`` is used as
+the URL prefix when generating a URL using
:meth:`pyramid.request.Request.static_url`.
-For example, :meth:`~pyramid.config.Configurator.add_static_view` may
-be fed a ``name`` argument which is ``http://example.com/images``:
+For example, :meth:`~pyramid.config.Configurator.add_static_view` may be fed a
+``name`` argument which is ``http://example.com/images``:
.. code-block:: python
:linenos:
# config is an instance of pyramid.config.Configurator
- config.add_static_view(name='http://example.com/images',
+ config.add_static_view(name='http://example.com/images',
path='mypackage:images')
-Because :meth:`~pyramid.config.Configurator.add_static_view` is provided with
-a ``name`` argument that is the URL ``http://example.com/images``, subsequent
-calls to :meth:`~pyramid.request.Request.static_url` with paths that start
-with the ``path`` argument passed to
+Because :meth:`~pyramid.config.Configurator.add_static_view` is provided with a
+``name`` argument that is the URL ``http://example.com/images``, subsequent
+calls to :meth:`~pyramid.request.Request.static_url` with paths that start with
+the ``path`` argument passed to
:meth:`~pyramid.config.Configurator.add_static_view` will generate a URL
-something like ``http://example.com/images/logo.png``. The external
-webserver listening on ``example.com`` must be itself configured to respond
-properly to such a request. The :meth:`~pyramid.request.Request.static_url`
-API is discussed in more detail later in this chapter.
+something like ``http://example.com/images/logo.png``. The external webserver
+listening on ``example.com`` must be itself configured to respond properly to
+such a request. The :meth:`~pyramid.request.Request.static_url` API is
+discussed in more detail later in this chapter.
.. index::
single: generating static asset urls
@@ -199,11 +199,11 @@ API is discussed in more detail later in this chapter.
Generating Static Asset URLs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When a :meth:`~pyramid.config.Configurator.add_static_view` method is used to
+When an :meth:`~pyramid.config.Configurator.add_static_view` method is used to
register a static asset directory, a special helper API named
:meth:`pyramid.request.Request.static_url` can be used to generate the
-appropriate URL for an asset that lives in one of the directories named by
-the static registration ``path`` attribute.
+appropriate URL for an asset that lives in one of the directories named by the
+static registration ``path`` attribute.
For example, let's assume you create a set of static declarations like so:
@@ -213,12 +213,12 @@ For example, let's assume you create a set of static declarations like so:
config.add_static_view(name='static1', path='mypackage:assets/1')
config.add_static_view(name='static2', path='mypackage:assets/2')
-These declarations create URL-accessible directories which have URLs that
-begin with ``/static1`` and ``/static2``, respectively. The assets in the
+These declarations create URL-accessible directories which have URLs that begin
+with ``/static1`` and ``/static2``, respectively. The assets in the
``assets/1`` directory of the ``mypackage`` package are consulted when a user
-visits a URL which begins with ``/static1``, and the assets in the
-``assets/2`` directory of the ``mypackage`` package are consulted when a user
-visits a URL which begins with ``/static2``.
+visits a URL which begins with ``/static1``, and the assets in the ``assets/2``
+directory of the ``mypackage`` package are consulted when a user visits a URL
+which begins with ``/static2``.
You needn't generate the URLs to static assets "by hand" in such a
configuration. Instead, use the :meth:`~pyramid.request.Request.static_url`
@@ -238,8 +238,8 @@ API to generate them for you. For example:
If the request "application URL" of the running system is
``http://example.com``, the ``css_url`` generated above would be:
-``http://example.com/static1/foo.css``. The ``js_url`` generated
-above would be ``http://example.com/static2/foo.js``.
+``http://example.com/static1/foo.css``. The ``js_url`` generated above would
+be ``http://example.com/static2/foo.js``.
One benefit of using the :meth:`~pyramid.request.Request.static_url` function
rather than constructing static URLs "by hand" is that if you need to change
@@ -249,19 +249,18 @@ resolve properly after the rename.
URLs may also be generated by :meth:`~pyramid.request.Request.static_url` to
static assets that live *outside* the :app:`Pyramid` application. This will
happen when the :meth:`~pyramid.config.Configurator.add_static_view` API
-associated with the path fed to :meth:`~pyramid.request.Request.static_url`
-is a *URL* instead of a view name. For example, the ``name`` argument may be
-``http://example.com`` while the ``path`` given may be
-``mypackage:images``:
+associated with the path fed to :meth:`~pyramid.request.Request.static_url` is
+a *URL* instead of a view name. For example, the ``name`` argument may be
+``http://example.com`` while the ``path`` given may be ``mypackage:images``:
.. code-block:: python
:linenos:
- config.add_static_view(name='http://example.com/images',
+ config.add_static_view(name='http://example.com/images',
path='mypackage:images')
-Under such a configuration, the URL generated by ``static_url`` for
-assets which begin with ``mypackage:images`` will be prefixed with
+Under such a configuration, the URL generated by ``static_url`` for assets
+which begin with ``mypackage:images`` will be prefixed with
``http://example.com/images``:
.. code-block:: python
@@ -271,16 +270,16 @@ assets which begin with ``mypackage:images`` will be prefixed with
# -> http://example.com/images/logo.png
Using :meth:`~pyramid.request.Request.static_url` in conjunction with a
-:meth:`~pyramid.config.Configurator.add_static_view` makes it possible
-to put static media on a separate webserver during production (if the
-``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is
-a URL), while keeping static media package-internal and served by the
-development webserver during development (if the ``name`` argument to
+:meth:`~pyramid.config.Configurator.add_static_view` makes it possible to put
+static media on a separate webserver during production (if the ``name``
+argument to :meth:`~pyramid.config.Configurator.add_static_view` is a URL),
+while keeping static media package-internal and served by the development
+webserver during development (if the ``name`` argument to
:meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix).
For example, we may define a :ref:`custom setting <adding_a_custom_setting>`
-named ``media_location`` which we can set to an external URL in production
-when our assets are hosted on a CDN.
+named ``media_location`` which we can set to an external URL in production when
+our assets are hosted on a CDN.
.. code-block:: python
:linenos:
@@ -305,18 +304,19 @@ It is also possible to serve assets that live outside of the source by
referring to an absolute path on the filesystem. There are two ways to
accomplish this.
-First, :meth:`~pyramid.config.Configurator.add_static_view`
-supports taking an absolute path directly instead of an asset spec. This works
-as expected, looking in the file or folder of files and serving them up at
-some URL within your application or externally. Unfortunately, this technique
-has a drawback that it is not possible to use the
-:meth:`~pyramid.request.Request.static_url` method to generate URLs, since it
-works based on an asset spec.
+First, :meth:`~pyramid.config.Configurator.add_static_view` supports taking an
+absolute path directly instead of an asset spec. This works as expected,
+looking in the file or folder of files and serving them up at some URL within
+your application or externally. Unfortunately, this technique has a drawback in
+that it is not possible to use the :meth:`~pyramid.request.Request.static_url`
+method to generate URLs, since it works based on an asset specification.
-The second approach, available in Pyramid 1.6+, uses the asset overriding
-APIs described in the :ref:`overriding_assets_section` section. It is then
-possible to configure a "dummy" package which then serves its file or folder
-from an absolute path.
+.. versionadded:: 1.6
+
+The second approach, available in Pyramid 1.6+, uses the asset overriding APIs
+described in the :ref:`overriding_assets_section` section. It is then possible
+to configure a "dummy" package which then serves its file or folder from an
+absolute path.
.. code-block:: python
@@ -325,13 +325,13 @@ from an absolute path.
override_with='/abs/path/to/images/')
From this configuration it is now possible to use
-:meth:`~pyramid.request.Request.static_url` to generate URLs to the data
-in the folder by doing something like
+:meth:`~pyramid.request.Request.static_url` to generate URLs to the data in the
+folder by doing something like
``request.static_url('myapp:static_images/foo.png')``. While it is not
necessary that the ``static_images`` file or folder actually exist in the
-``myapp`` package, it is important that the ``myapp`` portion points to a
-valid package. If the folder does exist then the overriden folder is given
-priority if the file's name exists in both locations.
+``myapp`` package, it is important that the ``myapp`` portion points to a valid
+package. If the folder does exist, then the overriden folder is given priority,
+if the file's name exists in both locations.
.. index::
single: Cache Busting
@@ -343,31 +343,30 @@ Cache Busting
.. versionadded:: 1.6
-In order to maximize performance of a web application, you generally want to
+In order to maximize performance of a web application, you generally want to
limit the number of times a particular client requests the same static asset.
-Ideally a client would cache a particular static asset "forever", requiring
-it to be sent to the client a single time. The HTTP protocol allows you to
-send headers with an HTTP response that can instruct a client to cache a
-particular asset for an amount of time. As long as the client has a copy of
-the asset in its cache and that cache hasn't expired, the client will use the
-cached copy rather than request a new copy from the server. The drawback to
-sending cache headers to the client for a static asset is that at some point
-the static asset may change, and then you'll want the client to load a new copy
-of the asset. Under normal circumstances you'd just need to wait for the
-client's cached copy to expire before they get the new version of the static
-resource.
-
-A commonly used workaround to this problem is a technique known as "cache
-busting". Cache busting schemes generally involve generating a URL for a
+Ideally a client would cache a particular static asset "forever", requiring it
+to be sent to the client a single time. The HTTP protocol allows you to send
+headers with an HTTP response that can instruct a client to cache a particular
+asset for an amount of time. As long as the client has a copy of the asset in
+its cache and that cache hasn't expired, the client will use the cached copy
+rather than request a new copy from the server. The drawback to sending cache
+headers to the client for a static asset is that at some point the static asset
+may change, and then you'll want the client to load a new copy of the asset.
+Under normal circumstances you'd just need to wait for the client's cached copy
+to expire before they get the new version of the static resource.
+
+A commonly used workaround to this problem is a technique known as "cache
+busting". Cache busting schemes generally involve generating a URL for a
static asset that changes when the static asset changes. This way headers can
be sent along with the static asset instructing the client to cache the asset
for a very long time. When a static asset is changed, the URL used to refer to
-it in a web page also changes, so the client sees it as a new resource and
-requests a copy, regardless of any caching policy set for the resource's old
+it in a web page also changes, so the client sees it as a new resource and
+requests the asset, regardless of any caching policy set for the resource's old
URL.
-:app:`Pyramid` can be configured to produce cache busting URLs for static
-assets by passing the optional argument, ``cachebust`` to
+:app:`Pyramid` can be configured to produce cache busting URLs for static
+assets by passing the optional argument, ``cachebust`` to
:meth:`~pyramid.config.Configurator.add_static_view`:
.. code-block:: python
@@ -377,7 +376,7 @@ assets by passing the optional argument, ``cachebust`` to
config.add_static_view(name='static', path='mypackage:folder/static',
cachebust=True)
-Setting the ``cachebust`` argument instructs :app:`Pyramid` to use a cache
+Setting the ``cachebust`` argument instructs :app:`Pyramid` to use a cache
busting scheme which adds the md5 checksum for a static asset as a path segment
in the asset's URL:
@@ -385,26 +384,26 @@ in the asset's URL:
:linenos:
js_url = request.static_url('mypackage:folder/static/js/myapp.js')
- # Returns: 'http://www.example.com/static/c9658b3c0a314a1ca21e5988e662a09e/js/myapp.js`
+ # Returns: 'http://www.example.com/static/c9658b3c0a314a1ca21e5988e662a09e/js/myapp.js'
When the asset changes, so will its md5 checksum, and therefore so will its
URL. Supplying the ``cachebust`` argument also causes the static view to set
headers instructing clients to cache the asset for ten years, unless the
-``max_cache_age`` argument is also passed, in which case that value is used.
+``cache_max_age`` argument is also passed, in which case that value is used.
-.. note::
+.. note::
- md5 checksums are cached in RAM so if you change a static resource without
+ md5 checksums are cached in RAM, so if you change a static resource without
restarting your application, you may still generate URLs with a stale md5
- checksum.
+ checksum.
Disabling the Cache Buster
~~~~~~~~~~~~~~~~~~~~~~~~~~
-It can be useful in some situations (e.g. development) to globally disable all
+It can be useful in some situations (e.g., development) to globally disable all
configured cache busters without changing calls to
-:meth:`~pyramid.config.Configurator.add_static_view`. To do this set the
-``PYRAMID_PREVENT_CACHEBUST`` environment variable or the
+:meth:`~pyramid.config.Configurator.add_static_view`. To do this set the
+``PYRAMID_PREVENT_CACHEBUST`` environment variable or the
``pyramid.prevent_cachebust`` configuration value to a true value.
Customizing the Cache Buster
@@ -420,8 +419,8 @@ Revisiting from the previous section:
cachebust=True)
Setting ``cachebust`` to ``True`` instructs :app:`Pyramid` to use a default
-cache busting implementation that should work for many situations. The
-``cachebust`` may be set to any object that implements the interface,
+cache busting implementation that should work for many situations. The
+``cachebust`` may be set to any object that implements the interface
:class:`~pyramid.interfaces.ICacheBuster`. The above configuration is exactly
equivalent to:
@@ -440,7 +439,7 @@ checksum token in the path portion of the asset's URL,
:class:`~pyramid.static.QueryStringMd5CacheBuster`, which adds an md5 checksum
token to the query string of the asset's URL, and
:class:`~pyramid.static.QueryStringConstantCacheBuster`, which adds an
-arbitrary token you provide to the query string of the asset's URL.
+arbitrary token you provide to the query string of the asset's URL.
In order to implement your own cache buster, you can write your own class from
scratch which implements the :class:`~pyramid.interfaces.ICacheBuster`
@@ -461,9 +460,9 @@ the hash of the currently checked out code:
class GitCacheBuster(PathSegmentCacheBuster):
"""
- Assuming your code is installed as a Git checkout, as opposed to as an
- egg from an egg repository like PYPI, you can use this cachebuster to
- get the current commit's SHA1 to use as the cache bust token.
+ Assuming your code is installed as a Git checkout, as opposed to an egg
+ from an egg repository like PYPI, you can use this cachebuster to get
+ the current commit's SHA1 to use as the cache bust token.
"""
def __init__(self):
here = os.path.dirname(os.path.abspath(__file__))
@@ -473,29 +472,29 @@ the hash of the currently checked out code:
def tokenize(self, pathspec):
return self.sha1
-
+
Choosing a Cache Buster
~~~~~~~~~~~~~~~~~~~~~~~
-The default cache buster implementation,
-:class:`~pyramid.static.PathSegmentMd5CacheBuster`, works very well assuming
+The default cache buster implementation,
+:class:`~pyramid.static.PathSegmentMd5CacheBuster`, works very well assuming
that you're using :app:`Pyramid` to serve your static assets. The md5 checksum
-is fine grained enough that browsers should only request new versions of
-specific assets that have changed. Many caching HTTP proxies will fail to
-cache a resource if the URL contains a query string. In general, therefore,
-you should prefer a cache busting strategy which modifies the path segment to
-a strategy which adds a query string.
+is fine grained enough that browsers should only request new versions of
+specific assets that have changed. Many caching HTTP proxies will fail to
+cache a resource if the URL contains a query string. In general, therefore,
+you should prefer a cache busting strategy which modifies the path segment to a
+strategy which adds a query string.
It is possible, however, that your static assets are being served by another
web server or externally on a CDN. In these cases modifying the path segment
for a static asset URL would cause the external service to fail to find the
-asset, causing your customer to get a 404. In these cases you would need to
-fall back to a cache buster which adds a query string. It is even possible
+asset, causing your customer to get a 404. In these cases you would need to
+fall back to a cache buster which adds a query string. It is even possible
that there isn't a copy of your static assets available to the :app:`Pyramid`
application, so a cache busting implementation that generates md5 checksums
-would fail since it can't access the assets. In such a case,
-:class:`~pyramid.static.QueryStringConstantCacheBuster` is a reasonable
-fallback. The following code would set up a cachebuster that just uses the
+would fail since it can't access the assets. In such a case,
+:class:`~pyramid.static.QueryStringConstantCacheBuster` is a reasonable
+fallback. The following code would set up a cachebuster that just uses the
time at start up as a cachebust token:
.. code-block:: python
@@ -505,13 +504,100 @@ time at start up as a cachebust token:
from pyramid.static import QueryStringConstantCacheBuster
config.add_static_view(
- name='http://mycdn.example.com/',
+ name='http://mycdn.example.com/',
path='mypackage:static',
cachebust=QueryStringConstantCacheBuster(str(time.time())))
.. index::
single: static assets view
+CSS and JavaScript source and cache busting
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Often one needs to refer to images and other static assets inside CSS and
+JavaScript files. If cache busting is active, the final static asset URL is not
+available until the static assets have been assembled. These URLs cannot be
+handwritten. Thus, when having static asset references in CSS and JavaScript,
+one needs to perform one of the following tasks.
+
+* Process the files by using a precompiler which rewrites URLs to their final
+ cache busted form.
+
+* Templatize JS and CSS, and call ``request.static_url()`` inside their
+ template code.
+
+* Pass static URL references to CSS and JavaScript via other means.
+
+Below are some simple approaches for CSS and JS programming which consider
+asset cache busting. These approaches do not require additional tools or
+packages.
+
+Relative cache busted URLs in CSS
++++++++++++++++++++++++++++++++++
+
+Consider a CSS file ``/static/theme/css/site.css`` which contains the following
+CSS code.
+
+.. code-block:: css
+
+ body {
+ background: url(/static/theme/img/background.jpg);
+ }
+
+Any changes to ``background.jpg`` would not appear to the visitor because the
+URL path is not cache busted as it is. Instead we would have to construct an
+URL to the background image with the default ``PathSegmentCacheBuster`` cache
+busting mechanism::
+
+ https://site/static/1eeb262c717/theme/img/background.jpg
+
+Every time the image is updated, the URL would need to be changed. It is not
+practical to write this non-human readable URL into a CSS file.
+
+However, the CSS file itself is cache busted and is located under the path for
+static assets. This lets us use relative references in our CSS to cache bust
+the image.
+
+.. code-block:: css
+
+ body {
+ background: url(../img/background.jpg);
+ }
+
+The browser would interpret this as having the CSS file hash in URL::
+
+ https://site/static/ab234b262c71/theme/css/../img/background.jpg
+
+The downside of this approach is that if the background image changes, one
+needs to bump the CSS file. The CSS file hash change signals the caches that
+the relative URL to the image in the CSS has been changed. When updating CSS
+and related image assets, updates usually happen hand in hand, so this does not
+add extra effort to theming workflow.
+
+Passing cache busted URLs to JavaScript
++++++++++++++++++++++++++++++++++++++++
+
+For JavaScript, one can pass static asset URLs as function arguments or
+globals. The globals can be generated in page template code, having access to
+the ``request.static_url()`` function.
+
+Below is a simple example of passing a cached busted image URL in the Jinja2
+template language. Put the following code into the ``<head>`` section of the
+relevant page.
+
+.. code-block:: html
+
+ <script>
+ window.assets.backgroundImage =
+ "{{ '/theme/img/background.jpg'|static_url() }}";
+ </script>
+
+Then in your main ``site.js`` file, put the following code.
+
+.. code-block:: javascript
+
+ var image = new Image(window.assets.backgroundImage);
+
.. _advanced_static:
Advanced: Serving Static Assets Using a View Callable
@@ -519,13 +605,13 @@ Advanced: Serving Static Assets Using a View Callable
For more flexibility, static assets can be served by a :term:`view callable`
which you register manually. For example, if you're using :term:`URL
-dispatch`, you may want static assets to only be available as a fallback if
-no previous route matches. Alternately, you might like to serve a particular
+dispatch`, you may want static assets to only be available as a fallback if no
+previous route matches. Alternatively, you might like to serve a particular
static asset manually, because its download requires authentication.
-Note that you cannot use the :meth:`~pyramid.request.Request.static_url` API
-to generate URLs against assets made accessible by registering a custom
-static view.
+Note that you cannot use the :meth:`~pyramid.request.Request.static_url` API to
+generate URLs against assets made accessible by registering a custom static
+view.
Root-Relative Custom Static View (URL Dispatch Only)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -539,19 +625,19 @@ its behavior is almost exactly the same once it's configured.
.. warning::
The following example *will not work* for applications that use
- :term:`traversal`, it will only work if you use :term:`URL dispatch`
+ :term:`traversal`; it will only work if you use :term:`URL dispatch`
exclusively. The root-relative route we'll be registering will always be
matched before traversal takes place, subverting any views registered via
``add_view`` (at least those without a ``route_name``). A
:class:`~pyramid.static.static_view` static view cannot be made
- root-relative when you use traversal unless it's registered as a
- :term:`Not Found View`.
+ root-relative when you use traversal unless it's registered as a :term:`Not
+ Found View`.
To serve files within a directory located on your filesystem at
``/path/to/static/dir`` as the result of a "catchall" route hanging from the
root that exists at the end of your routing table, create an instance of the
-:class:`~pyramid.static.static_view` class inside a ``static.py`` file in
-your application root as below.
+:class:`~pyramid.static.static_view` class inside a ``static.py`` file in your
+application root as below.
.. code-block:: python
:linenos:
@@ -561,10 +647,10 @@ your application root as below.
.. 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``.
+ 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``.
Subsequently, you may wire the files that are served by this view up to be
accessible as ``/<filename>`` using a configuration method in your
@@ -583,11 +669,11 @@ The special name ``*subpath`` above is used by the
:class:`~pyramid.static.static_view` view callable to signify the path of the
file relative to the directory you're serving.
-Registering A View Callable to Serve a "Static" Asset
+Registering a View Callable to Serve a "Static" Asset
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-You can register a simple view callable to serve a single static asset. To
-do so, do things "by hand". First define the view callable.
+You can register a simple view callable to serve a single static asset. To do
+so, do things "by hand". First define the view callable.
.. code-block:: python
:linenos:
@@ -600,17 +686,16 @@ do so, do things "by hand". First define the view callable.
icon = os.path.join(here, 'static', 'favicon.ico')
return FileResponse(icon, request=request)
-The above bit of code within ``favicon_view`` computes "here", which is a
-path relative to the Python file in which the function is defined. It then
-creates a :class:`pyramid.response.FileResponse` using the file path as the
-response's ``path`` argument and the request as the response's ``request``
-argument. :class:`pyramid.response.FileResponse` will serve the file as
-quickly as possible when it's used this way. It makes sure to set the right
-content length and content_type too based on the file extension of the file
-you pass.
+The above bit of code within ``favicon_view`` computes "here", which is a path
+relative to the Python file in which the function is defined. It then creates
+a :class:`pyramid.response.FileResponse` using the file path as the response's
+``path`` argument and the request as the response's ``request`` argument.
+:class:`pyramid.response.FileResponse` will serve the file as quickly as
+possible when it's used this way. It makes sure to set the right content
+length and content_type, too, based on the file extension of the file you pass.
-You might register such a view via configuration as a view callable that
-should be called as the result of a traversal:
+You might register such a view via configuration as a view callable that should
+be called as the result of a traversal:
.. code-block:: python
:linenos:
@@ -643,13 +728,12 @@ It can often be useful to override specific assets from "outside" a given
:app:`Pyramid` application more or less unchanged. However, some specific
template file owned by the application might have inappropriate HTML, or some
static asset (such as a logo file or some CSS file) might not be appropriate.
-You *could* just fork the application entirely, but it's often more
-convenient to just override the assets that are inappropriate and reuse the
-application "as is". This is particularly true when you reuse some "core"
-application over and over again for some set of customers (such as a CMS
-application, or some bug tracking application), and you want to make
-arbitrary visual modifications to a particular application deployment without
-forking the underlying code.
+You *could* just fork the application entirely, but it's often more convenient
+to just override the assets that are inappropriate and reuse the application
+"as is". This is particularly true when you reuse some "core" application over
+and over again for some set of customers (such as a CMS application, or some
+bug tracking application), and you want to make arbitrary visual modifications
+to a particular application deployment without forking the underlying code.
To this end, :app:`Pyramid` contains a feature that makes it possible to
"override" one asset with one or more other assets. In support of this
@@ -667,8 +751,8 @@ feature, a :term:`Configurator` API exists named
- A directory of static files served up by an instance of the
``pyramid.static.static_view`` helper class.
-- Any other asset (or set of assets) addressed by code that uses the
- setuptools :term:`pkg_resources` API.
+- Any other asset (or set of assets) addressed by code that uses the setuptools
+ :term:`pkg_resources` API.
.. index::
single: override_asset
@@ -678,8 +762,8 @@ feature, a :term:`Configurator` API exists named
The ``override_asset`` API
~~~~~~~~~~~~~~~~~~~~~~~~~~
-An individual call to :meth:`~pyramid.config.Configurator.override_asset`
-can override a single asset. For example:
+An individual call to :meth:`~pyramid.config.Configurator.override_asset` can
+override a single asset. For example:
.. code-block:: python
:linenos:
@@ -689,11 +773,11 @@ can override a single asset. For example:
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
-colon separator in a specification separates the *package name* from the
-*asset name*. The colon and the following asset name are optional. If they
-are not specified, the override attempts to resolve every lookup into a
-package from the directory of another package. For example:
+the ``override_asset`` API is called an :term:`asset specification`. The colon
+separator in a specification separates the *package name* from the *asset
+name*. The colon and the following asset name are optional. If they are not
+specified, the override attempts to resolve every lookup into a package from
+the directory of another package. For example:
.. code-block:: python
:linenos:
@@ -709,27 +793,25 @@ Individual subdirectories within a package can also be overridden:
config.override_asset(to_override='some.package:templates/',
override_with='another.package:othertemplates/')
-
-If you wish to override a directory with another directory, you *must*
-make sure to attach the slash to the end of both the ``to_override``
-specification and the ``override_with`` specification. If you fail to
-attach a slash to the end of a specification that points to a directory,
-you will get unexpected results.
+If you wish to override a directory with another directory, you *must* make
+sure to attach the slash to the end of both the ``to_override`` specification
+and the ``override_with`` specification. If you fail to attach a slash to the
+end of a specification that points to a directory, you will get unexpected
+results.
You cannot override a directory specification with a file specification, and
-vice versa: a startup error will occur if you try. You cannot override an
-asset with itself: a startup error will occur if you try.
+vice versa; a startup error will occur if you try. You cannot override an
+asset with itself; a startup error will occur if you try.
Only individual *package* assets may be overridden. Overrides will not
-traverse through subpackages within an overridden package. This means that
-if you want to override assets for both ``some.package:templates``, and
+traverse through subpackages within an overridden package. This means that if
+you want to override assets for both ``some.package:templates``, and
``some.package.views:templates``, you will need to register two overrides.
-The package name in a specification may start with a dot, meaning that
-the package is relative to the package in which the configuration
-construction file resides (or the ``package`` argument to the
-:class:`~pyramid.config.Configurator` class construction).
-For example:
+The package name in a specification may start with a dot, meaning that the
+package is relative to the package in which the configuration construction file
+resides (or the ``package`` argument to the
+:class:`~pyramid.config.Configurator` class construction). For example:
.. code-block:: python
:linenos:
@@ -737,18 +819,19 @@ For example:
config.override_asset(to_override='.subpackage:templates/',
override_with='another.package:templates/')
-Multiple calls to ``override_asset`` which name a shared ``to_override`` but
-a different ``override_with`` specification can be "stacked" to form a search
-path. The first asset that exists in the search path will be used; if no
-asset exists in the override path, the original asset is used.
+Multiple calls to ``override_asset`` which name a shared ``to_override`` but a
+different ``override_with`` specification can be "stacked" to form a search
+path. The first asset that exists in the search path will be used; if no asset
+exists in the override path, the original asset is used.
Asset overrides can actually override assets other than templates and static
files. Any software which uses the
:func:`pkg_resources.get_resource_filename`,
-:func:`pkg_resources.get_resource_stream` or
+:func:`pkg_resources.get_resource_stream`, or
:func:`pkg_resources.get_resource_string` APIs will obtain an overridden file
when an override is used.
-As of Pyramid 1.6, it is also possible to override an asset by supplying an
-absolute path to a file or directory. This may be useful if the assets are
-not distributed as part of a Python package.
+.. versionadded:: 1.6
+ As of Pyramid 1.6, it is also possible to override an asset by supplying an
+ absolute path to a file or directory. This may be useful if the assets are
+ not distributed as part of a Python package.
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
index 1fe2d9278..9db92b669 100644
--- a/docs/narr/commandline.rst
+++ b/docs/narr/commandline.rst
@@ -3,9 +3,8 @@
Command-Line Pyramid
====================
-Your :app:`Pyramid` application can be controlled and inspected using a
-variety of command-line utilities. These utilities are documented in this
-chapter.
+Your :app:`Pyramid` application can be controlled and inspected using a variety
+of command-line utilities. These utilities are documented in this chapter.
.. index::
pair: matching views; printing
@@ -17,15 +16,15 @@ Displaying Matching Views for a Given URL
-----------------------------------------
For a big application with several views, it can be hard to keep the view
-configuration details in your head, even if you defined all the views
-yourself. You can use the ``pviews`` command in a terminal window to
-print a summary of matching routes and views for a given URL in your
-application. The ``pviews`` command accepts two arguments. The first
-argument to ``pviews`` is the path to your application's ``.ini`` file and
-section name inside the ``.ini`` file which points to your application. This
-should be of the format ``config_file#section_name``. The second argument is
-the URL to test for matching views. The ``section_name`` may be omitted; if
-it is, it's considered to be ``main``.
+configuration details in your head, even if you defined all the views yourself.
+You can use the ``pviews`` command in a terminal window to print a summary of
+matching routes and views for a given URL in your application. The ``pviews``
+command accepts two arguments. The first argument to ``pviews`` is the path to
+your application's ``.ini`` file and section name inside the ``.ini`` file
+which points to your application. This should be of the format
+``config_file#section_name``. The second argument is the URL to test for
+matching views. The ``section_name`` may be omitted; if it is, it's considered
+to be ``main``.
Here is an example for a simple view configuration using :term:`traversal`:
@@ -44,12 +43,11 @@ Here is an example for a simple view configuration using :term:`traversal`:
tutorial.views.view_page
required permission = view
-The output always has the requested URL at the top and below that all the
-views that matched with their view configuration details. In this example
-only one view matches, so there is just a single *View* section. For each
-matching view, the full code path to the associated view callable is shown,
-along with any permissions and predicates that are part of that view
-configuration.
+The output always has the requested URL at the top and below that all the views
+that matched with their view configuration details. In this example only one
+view matches, so there is just a single *View* section. For each matching view,
+the full code path to the associated view callable is shown, along with any
+permissions and predicates that are part of that view configuration.
A more complex configuration might generate something like this:
@@ -99,12 +97,12 @@ A more complex configuration might generate something like this:
In this case, we are dealing with a :term:`URL dispatch` application. This
specific URL has two matching routes. The matching route information is
-displayed first, followed by any views that are associated with that route.
-As you can see from the second matching route output, a route can be
-associated with more than one view.
+displayed first, followed by any views that are associated with that route. As
+you can see from the second matching route output, a route can be associated
+with more than one view.
-For a URL that doesn't match any views, ``pviews`` will simply print out a
-*Not found* message.
+For a URL that doesn't match any views, ``pviews`` will simply print out a *Not
+found* message.
.. index::
@@ -118,17 +116,16 @@ For a URL that doesn't match any views, ``pviews`` will simply print out a
The Interactive Shell
---------------------
-Once you've installed your program for development using ``setup.py
-develop``, you can use an interactive Python shell to execute expressions in
-a Python environment exactly like the one that will be used when your
-application runs "for real". To do so, use the ``pshell`` command line
-utility.
+Once you've installed your program for development using ``setup.py develop``,
+you can use an interactive Python shell to execute expressions in a Python
+environment exactly like the one that will be used when your application runs
+"for real". To do so, use the ``pshell`` command line utility.
The argument to ``pshell`` follows the format ``config_file#section_name``
where ``config_file`` is the path to your application's ``.ini`` file and
``section_name`` is the ``app`` section name inside the ``.ini`` file which
-points to your application. For example, if your application ``.ini`` file
-might have a ``[app:main]`` section that looks like so:
+points to your application. For example, your application ``.ini`` file might
+have an ``[app:main]`` section that looks like so:
.. code-block:: ini
:linenos:
@@ -141,13 +138,13 @@ might have a ``[app:main]`` section that looks like so:
pyramid.debug_templates = true
pyramid.default_locale_name = en
-If so, you can use the following command to invoke a debug shell using the
-name ``main`` as a section name:
+If so, you can use the following command to invoke a debug shell using the name
+``main`` as a section name:
.. code-block:: text
$ $VENV/bin/pshell starter/development.ini#main
- Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
+ Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
[GCC 4.4.3] on linux2
Type "help" for more information.
@@ -172,9 +169,8 @@ name ``main`` as a section name:
The WSGI application that is loaded will be available in the shell as the
``app`` global. Also, if the application that is loaded is the :app:`Pyramid`
-app with no surrounding :term:`middleware`, the ``root`` object returned by
-the default :term:`root factory`, ``registry``, and ``request`` will be
-available.
+app with no surrounding :term:`middleware`, the ``root`` object returned by the
+default :term:`root factory`, ``registry``, and ``request`` will be available.
You can also simply rely on the ``main`` default section name by omitting any
hash after the filename:
@@ -193,22 +189,22 @@ Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows).
Extending the Shell
~~~~~~~~~~~~~~~~~~~
-It is convenient when using the interactive shell often to have some
-variables significant to your application already loaded as globals when
-you start the ``pshell``. To facilitate this, ``pshell`` will look for a
-special ``[pshell]`` section in your INI file and expose the subsequent
-key/value pairs to the shell. Each key is a variable name that will be
-global within the pshell session; each value is a :term:`dotted Python name`.
-If specified, the special key ``setup`` should be a :term:`dotted Python name`
-pointing to a callable that accepts the dictionary of globals that will
-be loaded into the shell. This allows for some custom initializing code
-to be executed each time the ``pshell`` is run. The ``setup`` callable
-can also be specified from the commandline using the ``--setup`` option
-which will override the key in the INI file.
-
-For example, you want to expose your model to the shell, along with the
-database session so that you can mutate the model on an actual database.
-Here, we'll assume your model is stored in the ``myapp.models`` package.
+It is convenient when using the interactive shell often to have some variables
+significant to your application already loaded as globals when you start the
+``pshell``. To facilitate this, ``pshell`` will look for a special ``[pshell]``
+section in your INI file and expose the subsequent key/value pairs to the
+shell. Each key is a variable name that will be global within the pshell
+session; each value is a :term:`dotted Python name`. If specified, the special
+key ``setup`` should be a :term:`dotted Python name` pointing to a callable
+that accepts the dictionary of globals that will be loaded into the shell. This
+allows for some custom initializing code to be executed each time the
+``pshell`` is run. The ``setup`` callable can also be specified from the
+commandline using the ``--setup`` option which will override the key in the INI
+file.
+
+For example, you want to expose your model to the shell along with the database
+session so that you can mutate the model on an actual database. Here, we'll
+assume your model is stored in the ``myapp.models`` package.
.. code-block:: ini
:linenos:
@@ -236,16 +232,16 @@ of the application to which we can easily submit requests.
env['request'].scheme = 'https'
env['testapp'] = TestApp(env['app'])
-When this INI file is loaded, the extra variables ``m``, ``session`` and
-``t`` will be available for use immediately. Since a ``setup`` callable
-was also specified, it is executed and a new variable ``testapp`` is
-exposed, and the request is configured to generate urls from the host
+When this INI file is loaded, the extra variables ``m``, ``session`` and ``t``
+will be available for use immediately. Since a ``setup`` callable was also
+specified, it is executed and a new variable ``testapp`` is exposed, and the
+request is configured to generate urls from the host
``http://www.example.com``. For example:
.. code-block:: text
$ $VENV/bin/pshell starter/development.ini
- Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
+ Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
[GCC 4.4.3] on linux2
Type "help" for more information.
@@ -276,18 +272,49 @@ exposed, and the request is configured to generate urls from the host
IPython or bpython
~~~~~~~~~~~~~~~~~~
-If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ and/or
-`bpython <http://bpython-interpreter.org/>`_ in
-the interpreter you use to invoke the ``pshell`` command, ``pshell`` will
-autodiscover and use the first one found, in this order:
-IPython, bpython, standard Python interpreter. However you could
-specifically invoke one of your choice with the ``-p choice`` or
-``--python-shell choice`` option.
+If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ and/or `bpython
+<http://bpython-interpreter.org/>`_ in the interpreter you use to invoke the
+``pshell`` command, ``pshell`` will autodiscover and use the first one found,
+in this order: IPython, bpython, standard Python interpreter. However you could
+specifically invoke your choice with the ``-p choice`` or ``--python-shell
+choice`` option.
.. code-block:: text
$ $VENV/bin/pshell -p ipython | bpython | python development.ini#MyProject
+Alternative Shells
+~~~~~~~~~~~~~~~~~~
+If you want to use a shell that isn't supported out of the box, you can
+introduce a new shell by registering an entry point in your setup.py:
+
+.. code-block:: python
+
+ setup(
+ entry_points = """\
+ [pyramid.pshell]
+ myshell=my_app:ptpython_shell_factory
+ """
+ )
+
+And then your shell factory should return a function that accepts two
+arguments, ``env`` and ``help``, which would look like this:
+
+.. code-block:: python
+
+ def ptpython_shell_factory():
+ from ptpython.repl import embed
+ def PTPShell(banner, **kwargs):
+ print(banner)
+ return embed(**kwargs)
+
+ def shell(env, help):
+ PTPShell(banner=help, locals=env)
+
+ return shell
+
+.. versionadded:: 1.6
+
.. index::
pair: routes; printing
single: proutes
@@ -297,14 +324,13 @@ specifically invoke one of your choice with the ``-p choice`` or
Displaying All Application Routes
---------------------------------
-You can use the ``proutes`` command in a terminal window to print a summary
-of routes related to your application. Much like the ``pshell``
-command (see :ref:`interactive_shell`), the ``proutes`` command
-accepts one argument with the format ``config_file#section_name``. The
-``config_file`` is the path to your application's ``.ini`` file, and
-``section_name`` is the ``app`` section name inside the ``.ini`` file which
-points to your application. By default, the ``section_name`` is ``main`` and
-can be omitted.
+You can use the ``proutes`` command in a terminal window to print a summary of
+routes related to your application. Much like the ``pshell`` command (see
+:ref:`interactive_shell`), the ``proutes`` command accepts one argument with
+the format ``config_file#section_name``. The ``config_file`` is the path to
+your application's ``.ini`` file, and ``section_name`` is the ``app`` section
+name inside the ``.ini`` file which points to your application. By default,
+the ``section_name`` is ``main`` and can be omitted.
For example:
@@ -312,8 +338,8 @@ For example:
:linenos:
$ $VENV/bin/proutes development.ini
- Name Pattern View
- ---- ------- ----
+ Name Pattern View Method
+ ---- ------- ---- ------
debugtoolbar /_debug_toolbar/*subpath <wsgiapp> *
__static/ /static/*subpath dummy_starter:static/ *
__static2/ /static2/*subpath /var/www/static/ *
@@ -325,22 +351,25 @@ For example:
multiview /multiview app1.standard_views.multiview GET,PATCH
not_post /not_post app1.standard_views.multview !POST,*
-``proutes`` generates a table with four columns: *Name*, *Pattern*, *Method*,
-and *View*. The items listed in the
-Name column are route names, the items listed in the Pattern column are route
-patterns, and the items listed in the View column are representations of the
-view callable that will be invoked when a request matches the associated
-route pattern. The view column may show ``<unknown>`` if no associated view
-callable could be found. If no routes are configured within your
-application, nothing will be printed to the console when ``proutes``
-is executed.
-
-It is convenient when using the ``proutes`` often to configure which columns
-and the order you would like to view them. To facilitate this, ``proutes`` will
-look for a special ``[proutes]`` section in your INI file and use those as
-defaults.
-
-For example you may remove request method and place the view first:
+``proutes`` generates a table with four columns: *Name*, *Pattern*, *View*, and
+*Method*. The items listed in the Name column are route names, the items
+listed in the Pattern column are route patterns, the items listed in the View
+column are representations of the view callable that will be invoked when a
+request matches the associated route pattern, and the items listed in the
+Method column are the request methods that are associated with the route name.
+The View column may show ``<unknown>`` if no associated view callable could be
+found. The Method column, for the route name, may show either ``<route
+mismatch>`` if the view callable does not accept any of the route's request
+methods, or ``*`` if the view callable will accept any of the route's request
+methods. If no routes are configured within your application, nothing will be
+printed to the console when ``proutes`` is executed.
+
+It is convenient when using the ``proutes`` command often to configure which
+columns and the order you would like to view them. To facilitate this,
+``proutes`` will look for a special ``[proutes]`` section in your ``.ini`` file
+and use those as defaults.
+
+For example you may remove the request method and place the view first:
.. code-block:: text
:linenos:
@@ -361,9 +390,10 @@ You can also separate the formats with commas or spaces:
[proutes]
format = view, name, pattern
-If you want to temporarily configure the columns and order there is the
-``--format`` which is a comma separated list of columns you want to include. The
-current available formats are ``name``, ``pattern``, ``view``, and ``method``.
+If you want to temporarily configure the columns and order, there is the
+argument ``--format``, which is a comma separated list of columns you want to
+include. The current available formats are ``name``, ``pattern``, ``view``, and
+``method``.
.. index::
@@ -375,17 +405,16 @@ current available formats are ``name``, ``pattern``, ``view``, and ``method``.
Displaying "Tweens"
-------------------
-A :term:`tween` is a bit of code that sits between the main Pyramid
-application request handler and the WSGI application which calls it. A user
-can get a representation of both the implicit tween ordering (the ordering
-specified by calls to :meth:`pyramid.config.Configurator.add_tween`) and the
-explicit tween ordering (specified by the ``pyramid.tweens`` configuration
-setting) orderings using the ``ptweens`` command. Tween factories
-will show up represented by their standard Python dotted name in the
-``ptweens`` output.
+A :term:`tween` is a bit of code that sits between the main Pyramid application
+request handler and the WSGI application which calls it. A user can get a
+representation of both the implicit tween ordering (the ordering specified by
+calls to :meth:`pyramid.config.Configurator.add_tween`) and the explicit tween
+ordering (specified by the ``pyramid.tweens`` configuration setting) using the
+``ptweens`` command. Tween factories will show up represented by their
+standard Python dotted name in the ``ptweens`` output.
-For example, here's the ``ptweens`` command run against a system
-configured without any explicit tweens:
+For example, here's the ``ptweens`` command run against a system configured
+without any explicit tweens:
.. code-block:: text
:linenos:
@@ -395,15 +424,15 @@ configured without any explicit tweens:
Implicit Tween Chain
- Position Name Alias
+ Position Name Alias
-------- ---- -----
- - INGRESS
0 pyramid_debugtoolbar.toolbar.toolbar_tween_factory pdbt
1 pyramid.tweens.excview_tween_factory excview
- - MAIN
-Here's the ``ptweens`` command run against a system configured *with*
-explicit tweens defined in its ``development.ini`` file:
+Here's the ``ptweens`` command run against a system configured *with* explicit
+tweens defined in its ``development.ini`` file:
.. code-block:: text
:linenos:
@@ -413,13 +442,13 @@ explicit tweens defined in its ``development.ini`` file:
Explicit Tween Chain (used)
- Position Name
- -------- ----
- - INGRESS
- 0 starter.tween_factory2
- 1 starter.tween_factory1
- 2 pyramid.tweens.excview_tween_factory
- - MAIN
+ Position Name
+ -------- ----
+ - INGRESS
+ 0 starter.tween_factory2
+ 1 starter.tween_factory1
+ 2 pyramid.tweens.excview_tween_factory
+ - MAIN
Implicit Tween Chain (not used)
@@ -430,9 +459,9 @@ explicit tweens defined in its ``development.ini`` file:
1 pyramid.tweens.excview_tween_factory
- MAIN
-Here's the application configuration section of the ``development.ini`` used
-by the above ``ptweens`` command which reports that the explicit tween chain
-is used:
+Here's the application configuration section of the ``development.ini`` used by
+the above ``ptweens`` command which reports that the explicit tween chain is
+used:
.. code-block:: ini
:linenos:
@@ -466,13 +495,13 @@ application and see the response body without starting a server.
There are two required arguments to ``prequest``:
-- The config file/section: follows the format ``config_file#section_name``
+- The config file/section: follows the format ``config_file#section_name``,
where ``config_file`` is the path to your application's ``.ini`` file and
``section_name`` is the ``app`` section name inside the ``.ini`` file. The
- ``section_name`` is optional, it defaults to ``main``. For example:
+ ``section_name`` is optional; it defaults to ``main``. For example:
``development.ini``.
-- The path: this should be the non-url-quoted path element of the URL to the
+- The path: this should be the non-URL-quoted path element of the URL to the
resource you'd like to be rendered on the server. For example, ``/``.
For example::
@@ -482,31 +511,31 @@ For example::
This will print the body of the response to the console on which it was
invoked.
-Several options are supported by ``prequest``. These should precede any
-config file name or URL.
+Several options are supported by ``prequest``. These should precede any config
+file name or URL.
-``prequest`` has a ``-d`` (aka ``--display-headers``) option which prints the
+``prequest`` has a ``-d`` (i.e., ``--display-headers``) option which prints the
status and headers returned by the server before the output::
$ $VENV/bin/prequest -d development.ini /
-This will print the status, then the headers, then the body of the response
-to the console.
+This will print the status, headers, and the body of the response to the
+console.
You can add request header values by using the ``--header`` option::
$ $VENV/bin/prequest --header=Host:example.com development.ini /
-Headers are added to the WSGI environment by converting them to their
-CGI/WSGI equivalents (e.g. ``Host=example.com`` will insert the ``HTTP_HOST``
-header variable as the value ``example.com``). Multiple ``--header`` options
-can be supplied. The special header value ``content-type`` sets the
-``CONTENT_TYPE`` in the WSGI environment.
+Headers are added to the WSGI environment by converting them to their CGI/WSGI
+equivalents (e.g., ``Host=example.com`` will insert the ``HTTP_HOST`` header
+variable as the value ``example.com``). Multiple ``--header`` options can be
+supplied. The special header value ``content-type`` sets the ``CONTENT_TYPE``
+in the WSGI environment.
-By default, ``prequest`` sends a ``GET`` request. You can change this by
-using the ``-m`` (aka ``--method``) option. ``GET``, ``HEAD``, ``POST`` and
-``DELETE`` are currently supported. When you use ``POST``, the standard
-input of the ``prequest`` process is used as the ``POST`` body::
+By default, ``prequest`` sends a ``GET`` request. You can change this by using
+the ``-m`` (aka ``--method``) option. ``GET``, ``HEAD``, ``POST``, and
+``DELETE`` are currently supported. When you use ``POST``, the standard input
+of the ``prequest`` process is used as the ``POST`` body::
$ $VENV/bin/prequest -mPOST development.ini / < somefile
@@ -515,28 +544,28 @@ Using Custom Arguments to Python when Running ``p*`` Scripts
.. versionadded:: 1.5
-Each of Pyramid's console scripts (``pserve``, ``pviews``, etc) can be run
+Each of Pyramid's console scripts (``pserve``, ``pviews``, etc.) can be run
directly using ``python -m``, allowing custom arguments to be sent to the
-python interpreter at runtime. For example::
+Python interpreter at runtime. For example::
python -3 -m pyramid.scripts.pserve development.ini
-Showing All Installed Distributions and their Versions
+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::
+You can use the ``pdistreport`` command to show the :app:`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
+ 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 ...
@@ -551,35 +580,33 @@ Writing a Script
----------------
All web applications are, at their hearts, systems which accept a request and
-return a response. When a request is accepted by a :app:`Pyramid`
-application, the system receives state from the request which is later relied
-on by your application code. For example, one :term:`view callable` may assume
-it's working against a request that has a ``request.matchdict`` of a
-particular composition, while another assumes a different composition of the
-matchdict.
+return a response. When a request is accepted by a :app:`Pyramid` application,
+the system receives state from the request which is later relied on by your
+application code. For example, one :term:`view callable` may assume it's
+working against a request that has a ``request.matchdict`` of a particular
+composition, while another assumes a different composition of the matchdict.
In the meantime, it's convenient to be able to write a Python script that can
-work "in a Pyramid environment", for instance to update database tables used
-by your :app:`Pyramid` application. But a "real" Pyramid environment doesn't
-have a completely static state independent of a request; your application
-(and Pyramid itself) is almost always reliant on being able to obtain
-information from a request. When you run a Python script that simply imports
-code from your application and tries to run it, there just is no request
-data, because there isn't any real web request. Therefore some parts of your
-application and some Pyramid APIs will not work.
+work "in a Pyramid environment", for instance to update database tables used by
+your :app:`Pyramid` application. But a "real" Pyramid environment doesn't have
+a completely static state independent of a request; your application (and
+Pyramid itself) is almost always reliant on being able to obtain information
+from a request. When you run a Python script that simply imports code from
+your application and tries to run it, there just is no request data, because
+there isn't any real web request. Therefore some parts of your application and
+some Pyramid APIs will not work.
For this reason, :app:`Pyramid` makes it possible to run a script in an
environment much like the environment produced when a particular
:term:`request` reaches your :app:`Pyramid` application. This is achieved by
-using the :func:`pyramid.paster.bootstrap` command in the body of your
-script.
+using the :func:`pyramid.paster.bootstrap` command in the body of your script.
.. versionadded:: 1.1
:func:`pyramid.paster.bootstrap`
In the simplest case, :func:`pyramid.paster.bootstrap` can be used with a
single argument, which accepts the :term:`PasteDeploy` ``.ini`` file
-representing Pyramid your application configuration as a single argument:
+representing your Pyramid application's configuration as a single argument:
.. code-block:: python
@@ -615,14 +642,13 @@ registry
closer
- A parameterless callable that can be used to pop an internal
- :app:`Pyramid` threadlocal stack (used by
- :func:`pyramid.threadlocal.get_current_registry` and
- :func:`pyramid.threadlocal.get_current_request`) when your scripting job
- is finished.
+ A parameterless callable that can be used to pop an internal :app:`Pyramid`
+ threadlocal stack (used by :func:`pyramid.threadlocal.get_current_registry`
+ and :func:`pyramid.threadlocal.get_current_request`) when your scripting
+ job is finished.
-Let's assume that the ``/path/to/my/development.ini`` file used in the
-example above looks like so:
+Let's assume that the ``/path/to/my/development.ini`` file used in the example
+above looks like so:
.. code-block:: ini
@@ -639,15 +665,15 @@ example above looks like so:
use = egg:MyProject
The configuration loaded by the above bootstrap example will use the
-configuration implied by the ``[pipeline:main]`` section of your
-configuration file by default. Specifying ``/path/to/my/development.ini`` is
-logically equivalent to specifying ``/path/to/my/development.ini#main``. In
-this case, we'll be using a configuration that includes an ``app`` object
-which is wrapped in the Paste "translogger" :term:`middleware` (which logs
-requests to the console).
+configuration implied by the ``[pipeline:main]`` section of your configuration
+file by default. Specifying ``/path/to/my/development.ini`` is logically
+equivalent to specifying ``/path/to/my/development.ini#main``. In this case,
+we'll be using a configuration that includes an ``app`` object which is wrapped
+in the Paste "translogger" :term:`middleware` (which logs requests to the
+console).
-You can also specify a particular *section* of the PasteDeploy ``.ini`` file
-to load instead of ``main``:
+You can also specify a particular *section* of the PasteDeploy ``.ini`` file to
+load instead of ``main``:
.. code-block:: python
@@ -664,9 +690,9 @@ Changing the Request
~~~~~~~~~~~~~~~~~~~~
By default, Pyramid will generate a request object in the ``env`` dictionary
-for the URL ``http://localhost:80/``. This means that any URLs generated
-by Pyramid during the execution of your script will be anchored here. This
-is generally not what you want.
+for the URL ``http://localhost:80/``. This means that any URLs generated by
+Pyramid during the execution of your script will be anchored here. This is
+generally not what you want.
So how do we make Pyramid generate the correct URLs?
@@ -678,10 +704,10 @@ Assuming that you have a route configured in your application like so:
You need to inform the Pyramid environment that the WSGI application is
handling requests from a certain base. For example, we want to simulate
-mounting our application at `https://example.com/prefix`, to ensure that
-the generated URLs are correct for our deployment. This can be done by
-either mutating the resulting request object, or more simply by constructing
-the desired request and passing it into :func:`~pyramid.paster.bootstrap`:
+mounting our application at `https://example.com/prefix`, to ensure that the
+generated URLs are correct for our deployment. This can be done by either
+mutating the resulting request object, or more simply by constructing the
+desired request and passing it into :func:`~pyramid.paster.bootstrap`:
.. code-block:: python
@@ -740,43 +766,43 @@ Making Your Script into a Console Script
----------------------------------------
A "console script" is :term:`setuptools` terminology for a script that gets
-installed into the ``bin`` directory of a Python :term:`virtualenv` (or
-"base" Python environment) when a :term:`distribution` which houses that
-script is installed. Because it's installed into the ``bin`` directory of a
-virtualenv when the distribution is installed, it's a convenient way to
-package and distribute functionality that you can call from the command-line.
-It's often more convenient to create a console script than it is to create a
-``.py`` script and instruct people to call it with the "right" Python
-interpreter. A console script generates a file that lives in ``bin``, and when it's
-invoked it will always use the "right" Python environment, which means it
-will always be invoked in an environment where all the libraries it needs
-(such as Pyramid) are available.
+installed into the ``bin`` directory of a Python :term:`virtualenv` (or "base"
+Python environment) when a :term:`distribution` which houses that script is
+installed. Because it's installed into the ``bin`` directory of a virtualenv
+when the distribution is installed, it's a convenient way to package and
+distribute functionality that you can call from the command-line. It's often
+more convenient to create a console script than it is to create a ``.py``
+script and instruct people to call it with the "right" Python interpreter. A
+console script generates a file that lives in ``bin``, and when it's invoked it
+will always use the "right" Python environment, which means it will always be
+invoked in an environment where all the libraries it needs (such as Pyramid)
+are available.
In general, you can make your script into a console script by doing the
following:
- Use an existing distribution (such as one you've already created via
- ``pcreate``) or create a new distribution that possesses at least one
- package or module. It should, within any module within the distribution,
- house a callable (usually a function) that takes no arguments and which
- runs any of the code you wish to run.
+ ``pcreate``) or create a new distribution that possesses at least one package
+ or module. It should, within any module within the distribution, house a
+ callable (usually a function) that takes no arguments and which runs any of
+ the code you wish to run.
- Add a ``[console_scripts]`` section to the ``entry_points`` argument of the
- distribution which creates a mapping between a script name and a dotted
- name representing the callable you added to your distribution.
+ distribution which creates a mapping between a script name and a dotted name
+ representing the callable you added to your distribution.
- Run ``setup.py develop``, ``setup.py install``, or ``easy_install`` to get
- your distribution reinstalled. When you reinstall your distribution, a
- file representing the script that you named in the last step will be in the
- ``bin`` directory of the virtualenv in which you installed the
- distribution. It will be executable. Invoking it from a terminal will
- execute your callable.
+ your distribution reinstalled. When you reinstall your distribution, a file
+ representing the script that you named in the last step will be in the
+ ``bin`` directory of the virtualenv in which you installed the distribution.
+ It will be executable. Invoking it from a terminal will execute your
+ callable.
As an example, let's create some code that can be invoked by a console script
-that prints the deployment settings of a Pyramid application. To do so,
-we'll pretend you have a distribution with a package in it named
-``myproject``. Within this package, we'll pretend you've added a
-``scripts.py`` module which contains the following code:
+that prints the deployment settings of a Pyramid application. To do so, we'll
+pretend you have a distribution with a package in it named ``myproject``.
+Within this package, we'll pretend you've added a ``scripts.py`` module which
+contains the following code:
.. code-block:: python
:linenos:
@@ -835,7 +861,7 @@ defined in that config file.
After adding this script to the package, you'll need to tell your
distribution's ``setup.py`` about its existence. Within your distribution's
-top-level directory your ``setup.py`` file will look something like this:
+top-level directory, your ``setup.py`` file will look something like this:
.. code-block:: python
:linenos:
@@ -878,9 +904,9 @@ top-level directory your ``setup.py`` file will look something like this:
""",
)
-We're going to change the setup.py file to add an ``[console_scripts]``
-section with in the ``entry_points`` string. Within this section, you should
-specify a ``scriptname = dotted.path.to:yourfunction`` line. For example::
+We're going to change the setup.py file to add a ``[console_scripts]`` section
+within the ``entry_points`` string. Within this section, you should specify a
+``scriptname = dotted.path.to:yourfunction`` line. For example::
[console_scripts]
show_settings = myproject.scripts:settings_show
@@ -888,9 +914,9 @@ specify a ``scriptname = dotted.path.to:yourfunction`` line. For example::
The ``show_settings`` name will be the name of the script that is installed
into ``bin``. The colon (``:``) between ``myproject.scripts`` and
``settings_show`` above indicates that ``myproject.scripts`` is a Python
-module, and ``settings_show`` is the function in that module which contains
-the code you'd like to run as the result of someone invoking the
-``show_settings`` script from their command line.
+module, and ``settings_show`` is the function in that module which contains the
+code you'd like to run as the result of someone invoking the ``show_settings``
+script from their command line.
The result will be something like:
@@ -937,29 +963,28 @@ The result will be something like:
""",
)
-Once you've done this, invoking ``$$VENV/bin/python setup.py
-develop`` will install a file named ``show_settings`` into the
-``$somevirtualenv/bin`` directory with a small bit of Python code that points
-to your entry point. It will be executable. Running it without any
-arguments will print an error and exit. Running it with a single argument
-that is the path of a config file will print the settings. Running it with
-an ``--omit=foo`` argument will omit the settings that have keys that start
-with ``foo``. Running it with two "omit" options (e.g. ``--omit=foo
---omit=bar``) will omit all settings that have keys that start with either
-``foo`` or ``bar``::
+Once you've done this, invoking ``$$VENV/bin/python setup.py develop`` will
+install a file named ``show_settings`` into the ``$somevirtualenv/bin``
+directory with a small bit of Python code that points to your entry point. It
+will be executable. Running it without any arguments will print an error and
+exit. Running it with a single argument that is the path of a config file will
+print the settings. Running it with an ``--omit=foo`` argument will omit the
+settings that have keys that start with ``foo``. Running it with two "omit"
+options (e.g., ``--omit=foo --omit=bar``) will omit all settings that have keys
+that start with either ``foo`` or ``bar``::
$ $VENV/bin/show_settings development.ini --omit=pyramid --omit=debugtoolbar
- debug_routematch False
- debug_templates True
- reload_templates True
- mako.directories []
- debug_notfound False
- default_locale_name en
- reload_resources False
- debug_authorization False
- reload_assets False
- prevent_http_cache False
-
-Pyramid's ``pserve``, ``pcreate``, ``pshell``, ``prequest``, ``ptweens`` and
+ debug_routematch False
+ debug_templates True
+ reload_templates True
+ mako.directories []
+ debug_notfound False
+ default_locale_name en
+ reload_resources False
+ debug_authorization False
+ reload_assets False
+ prevent_http_cache False
+
+Pyramid's ``pserve``, ``pcreate``, ``pshell``, ``prequest``, ``ptweens``, and
other ``p*`` scripts are implemented as console scripts. When you invoke one
of those, you are using a console script.
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst
index f7fa94daf..cde166b21 100644
--- a/docs/narr/configuration.rst
+++ b/docs/narr/configuration.rst
@@ -3,27 +3,26 @@
.. _configuration_narr:
-Application Configuration
+Application Configuration
=========================
Most people already understand "configuration" as settings that influence the
-operation of an application. For instance, it's easy to think of the values
-in a ``.ini`` file parsed at application startup time as "configuration".
-However, if you're reasonably open-minded, it's easy to think of *code* as
-configuration too. Since Pyramid, like most other web application platforms,
-is a *framework*, it calls into code that you write (as opposed to a
-*library*, which is code that exists purely for you to call). The act of
-plugging application code that you've written into :app:`Pyramid` is also
-referred to within this documentation as "configuration"; you are configuring
+operation of an application. For instance, it's easy to think of the values in
+a ``.ini`` file parsed at application startup time as "configuration". However,
+if you're reasonably open-minded, it's easy to think of *code* as configuration
+too. Since Pyramid, like most other web application platforms, is a
+*framework*, it calls into code that you write (as opposed to a *library*,
+which is code that exists purely for you to call). The act of plugging
+application code that you've written into :app:`Pyramid` is also referred to
+within this documentation as "configuration"; you are configuring
:app:`Pyramid` to call the code that makes up your application.
.. seealso::
For information on ``.ini`` files for Pyramid applications see the
:ref:`startup_chapter` chapter.
-There are two ways to configure a :app:`Pyramid` application:
-:term:`imperative configuration` and :term:`declarative configuration`. Both
-are described below.
+There are two ways to configure a :app:`Pyramid` application: :term:`imperative
+configuration` and :term:`declarative configuration`. Both are described below.
.. index::
single: imperative configuration
@@ -33,9 +32,9 @@ are described below.
Imperative Configuration
------------------------
-"Imperative configuration" just means configuration done by Python
-statements, one after the next. Here's one of the simplest :app:`Pyramid`
-applications, configured imperatively:
+"Imperative configuration" just means configuration done by Python statements,
+one after the next. Here's one of the simplest :app:`Pyramid` applications,
+configured imperatively:
.. code-block:: python
:linenos:
@@ -57,9 +56,9 @@ applications, configured imperatively:
We won't talk much about what this application does yet. Just note that the
"configuration' statements take place underneath the ``if __name__ ==
'__main__':`` stanza in the form of method calls on a :term:`Configurator`
-object (e.g. ``config.add_view(...)``). These statements take place one
-after the other, and are executed in order, so the full power of Python,
-including conditionals, can be employed in this mode of configuration.
+object (e.g., ``config.add_view(...)``). These statements take place one after
+the other, and are executed in order, so the full power of Python, including
+conditionals, can be employed in this mode of configuration.
.. index::
single: view_config
@@ -72,13 +71,13 @@ Declarative Configuration
-------------------------
It's sometimes painful to have all configuration done by imperative code,
-because often the code for a single application may live in many files. If
-the configuration is centralized in one place, you'll need to have at least
-two files open at once to see the "big picture": the file that represents the
-configuration, and the file that contains the implementation objects
-referenced by the configuration. To avoid this, :app:`Pyramid` allows you to
-insert :term:`configuration decoration` statements very close to code that is
-referred to by the declaration itself. For example:
+because often the code for a single application may live in many files. If the
+configuration is centralized in one place, you'll need to have at least two
+files open at once to see the "big picture": the file that represents the
+configuration, and the file that contains the implementation objects referenced
+by the configuration. To avoid this, :app:`Pyramid` allows you to insert
+:term:`configuration decoration` statements very close to code that is referred
+to by the declaration itself. For example:
.. code-block:: python
:linenos:
@@ -90,20 +89,19 @@ referred to by the declaration itself. For example:
def hello(request):
return Response('Hello')
-The mere existence of configuration decoration doesn't cause any
-configuration registration to be performed. Before it has any effect on the
-configuration of a :app:`Pyramid` application, a configuration decoration
-within application code must be found through a process known as a
-:term:`scan`.
+The mere existence of configuration decoration doesn't cause any configuration
+registration to be performed. Before it has any effect on the configuration of
+a :app:`Pyramid` application, a configuration decoration within application
+code must be found through a process known as a :term:`scan`.
For example, the :class:`pyramid.view.view_config` decorator in the code
-example above adds an attribute to the ``hello`` function, making it
-available for a :term:`scan` to find it later.
+example above adds an attribute to the ``hello`` function, making it available
+for a :term:`scan` to find it later.
-A :term:`scan` of a :term:`module` or a :term:`package` and its subpackages
-for decorations happens when the :meth:`pyramid.config.Configurator.scan`
-method is invoked: scanning implies searching for configuration declarations
-in a package and its subpackages. For example:
+A :term:`scan` of a :term:`module` or a :term:`package` and its subpackages for
+decorations happens when the :meth:`pyramid.config.Configurator.scan` method is
+invoked: scanning implies searching for configuration declarations in a package
+and its subpackages. For example:
.. code-block:: python
:linenos:
@@ -125,16 +123,16 @@ in a package and its subpackages. For example:
server.serve_forever()
The scanning machinery imports each module and subpackage in a package or
-module recursively, looking for special attributes attached to objects
-defined within a module. These special attributes are typically attached to
-code via the use of a :term:`decorator`. For example, the
+module recursively, looking for special attributes attached to objects defined
+within a module. These special attributes are typically attached to code via
+the use of a :term:`decorator`. For example, the
:class:`~pyramid.view.view_config` decorator can be attached to a function or
instance method.
-Once scanning is invoked, and :term:`configuration decoration` is found by
-the scanner, a set of calls are made to a :term:`Configurator` on your
-behalf: these calls replace the need to add imperative configuration
-statements that don't live near the code being configured.
+Once scanning is invoked, and :term:`configuration decoration` is found by the
+scanner, a set of calls are made to a :term:`Configurator` on your behalf.
+These calls replace the need to add imperative configuration statements that
+don't live near the code being configured.
The combination of :term:`configuration decoration` and the invocation of a
:term:`scan` is collectively known as :term:`declarative configuration`.
@@ -150,7 +148,7 @@ In the example above, the scanner translates the arguments to
Summary
-------
-There are two ways to configure a :app:`Pyramid` application: declaratively
-and imperatively. You can choose the mode you're most comfortable with; both
-are completely equivalent. Examples in this documentation will use both
-modes interchangeably.
+There are two ways to configure a :app:`Pyramid` application: declaratively and
+imperatively. You can choose the mode with which you're most comfortable; both
+are completely equivalent. Examples in this documentation will use both modes
+interchangeably.
diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst
index 0b06fb80b..743266d2c 100644
--- a/docs/narr/environment.rst
+++ b/docs/narr/environment.rst
@@ -21,39 +21,36 @@
Environment Variables and ``.ini`` File Settings
================================================
-:app:`Pyramid` behavior can be configured through a combination of
-operating system environment variables and ``.ini`` configuration file
-application section settings. The meaning of the environment
-variables and the configuration file settings overlap.
-
-.. note:: Where a configuration file setting exists with the same
- meaning as an environment variable, and both are present at
- application startup time, the environment variable setting
- takes precedence.
-
-The term "configuration file setting name" refers to a key in the
-``.ini`` configuration for your application. The configuration file
-setting names documented in this chapter are reserved for
-:app:`Pyramid` use. You should not use them to indicate
-application-specific configuration settings.
+:app:`Pyramid` behavior can be configured through a combination of operating
+system environment variables and ``.ini`` configuration file application
+section settings. The meaning of the environment variables and the
+configuration file settings overlap.
+
+.. note::
+ Where a configuration file setting exists with the same meaning as an
+ environment variable, and both are present at application startup time, the
+ environment variable setting takes precedence.
+
+The term "configuration file setting name" refers to a key in the ``.ini``
+configuration for your application. The configuration file setting names
+documented in this chapter are reserved for :app:`Pyramid` use. You should not
+use them to indicate application-specific configuration settings.
Reloading Templates
-------------------
-When this value is true, templates are automatically reloaded whenever
-they are modified without restarting the application, so you can see
-changes to templates take effect immediately during development. This
-flag is meaningful to Chameleon and Mako templates, as well as most
-third-party template rendering extensions.
-
-+---------------------------------+--------------------------------+
-| Environment Variable Name | Config File Setting Name |
-+=================================+================================+
-| ``PYRAMID_RELOAD_TEMPLATES`` | ``pyramid.reload_templates`` |
-| | or ``reload_templates`` |
-| | |
-| | |
-+---------------------------------+--------------------------------+
+When this value is true, templates are automatically reloaded whenever they are
+modified without restarting the application, so you can see changes to
+templates take effect immediately during development. This flag is meaningful
+to Chameleon and Mako templates, as well as most third-party template rendering
+extensions.
+
++-------------------------------+--------------------------------+
+| Environment Variable Name | Config File Setting Name |
++===============================+================================+
+| ``PYRAMID_RELOAD_TEMPLATES`` | ``pyramid.reload_templates`` |
+| | or ``reload_templates`` |
++-------------------------------+--------------------------------+
Reloading Assets
----------------
@@ -64,24 +61,22 @@ Don't cache any asset file data when this value is true.
See also :ref:`overriding_assets_section`.
-+---------------------------------+-----------------------------+
-| Environment Variable Name | Config File Setting Name |
-+=================================+=============================+
-| ``PYRAMID_RELOAD_ASSETS`` | ``pyramid.reload_assets`` |
-| | or ``reload_assets`` |
-| | |
-| | |
-+---------------------------------+-----------------------------+
++----------------------------+-----------------------------+
+| Environment Variable Name | Config File Setting Name |
++============================+=============================+
+| ``PYRAMID_RELOAD_ASSETS`` | ``pyramid.reload_assets`` |
+| | or ``reload_assets`` |
++----------------------------+-----------------------------+
-.. note:: For backwards compatibility purposes, aliases can be
- used for configurating asset reloading: ``PYRAMID_RELOAD_RESOURCES`` (envvar)
- and ``pyramid.reload_resources`` (config file).
+.. note:: For backwards compatibility purposes, aliases can be used for
+ configuring asset reloading: ``PYRAMID_RELOAD_RESOURCES`` (envvar) and
+ ``pyramid.reload_resources`` (config file).
Debugging Authorization
-----------------------
-Print view authorization failure and success information to stderr
-when this value is true.
+Print view authorization failure and success information to stderr when this
+value is true.
.. seealso::
@@ -92,28 +87,24 @@ when this value is true.
+=================================+===================================+
| ``PYRAMID_DEBUG_AUTHORIZATION`` | ``pyramid.debug_authorization`` |
| | or ``debug_authorization`` |
-| | |
-| | |
+---------------------------------+-----------------------------------+
Debugging Not Found Errors
--------------------------
-Print view-related ``NotFound`` debug messages to stderr
-when this value is true.
+Print view-related ``NotFound`` debug messages to stderr when this value is
+true.
.. seealso::
See also :ref:`debug_notfound_section`.
-+---------------------------------+------------------------------+
-| Environment Variable Name | Config File Setting Name |
-+=================================+==============================+
-| ``PYRAMID_DEBUG_NOTFOUND`` | ``pyramid.debug_notfound`` |
-| | or ``debug_notfound`` |
-| | |
-| | |
-+---------------------------------+------------------------------+
++----------------------------+------------------------------+
+| Environment Variable Name | Config File Setting Name |
++============================+==============================+
+| ``PYRAMID_DEBUG_NOTFOUND`` | ``pyramid.debug_notfound`` |
+| | or ``debug_notfound`` |
++----------------------------+------------------------------+
Debugging Route Matching
------------------------
@@ -125,24 +116,22 @@ this value is true.
See also :ref:`debug_routematch_section`.
-+---------------------------------+--------------------------------+
-| Environment Variable Name | Config File Setting Name |
-+=================================+================================+
-| ``PYRAMID_DEBUG_ROUTEMATCH`` | ``pyramid.debug_routematch`` |
-| | or ``debug_routematch`` |
-| | |
-| | |
-+---------------------------------+--------------------------------+
++------------------------------+--------------------------------+
+| Environment Variable Name | Config File Setting Name |
++==============================+================================+
+| ``PYRAMID_DEBUG_ROUTEMATCH`` | ``pyramid.debug_routematch`` |
+| | or ``debug_routematch`` |
++------------------------------+--------------------------------+
.. _preventing_http_caching:
Preventing HTTP Caching
-------------------------
+-----------------------
Prevent the ``http_cache`` view configuration argument from having any effect
-globally in this process when this value is true. No http caching-related
-response headers will be set by the Pyramid ``http_cache`` view configuration
-feature when this is true.
+globally in this process when this value is true. No HTTP caching-related
+response headers will be set by the :app:`Pyramid` ``http_cache`` view
+configuration feature when this is true.
.. seealso::
@@ -153,8 +142,6 @@ feature when this is true.
+=================================+==================================+
| ``PYRAMID_PREVENT_HTTP_CACHE`` | ``pyramid.prevent_http_cache`` |
| | or ``prevent_http_cache`` |
-| | |
-| | |
+---------------------------------+----------------------------------+
Preventing Cache Busting
@@ -162,7 +149,7 @@ Preventing Cache Busting
Prevent the ``cachebust`` static view configuration argument from having any
effect globally in this process when this value is true. No cache buster will
-be configured or used when this is true.
+be configured or used when this is true.
.. versionadded:: 1.6
@@ -175,8 +162,6 @@ be configured or used when this is true.
+=================================+==================================+
| ``PYRAMID_PREVENT_CACHEBUST`` | ``pyramid.prevent_cachebust`` |
| | or ``prevent_cachebust`` |
-| | |
-| | |
+---------------------------------+----------------------------------+
Debugging All
@@ -184,36 +169,32 @@ Debugging All
Turns on all ``debug*`` settings.
-+---------------------------------+-----------------------------+
-| Environment Variable Name | Config File Setting Name |
-+=================================+=============================+
-| ``PYRAMID_DEBUG_ALL`` | ``pyramid.debug_all`` |
-| | or ``debug_all`` |
-| | |
-| | |
-+---------------------------------+-----------------------------+
++----------------------------+---------------------------+
+| Environment Variable Name | Config File Setting Name |
++============================+===========================+
+| ``PYRAMID_DEBUG_ALL`` | ``pyramid.debug_all`` |
+| | or ``debug_all`` |
++----------------------------+---------------------------+
Reloading All
-------------
Turns on all ``reload*`` settings.
-+---------------------------------+-----------------------------+
-| Environment Variable Name | Config File Setting Name |
-+=================================+=============================+
-| ``PYRAMID_RELOAD_ALL`` | ``pyramid.reload_all`` |
-| | or ``reload_all`` |
-| | |
-| | |
-+---------------------------------+-----------------------------+
++---------------------------+----------------------------+
+| Environment Variable Name | Config File Setting Name |
++===========================+============================+
+| ``PYRAMID_RELOAD_ALL`` | ``pyramid.reload_all`` or |
+| | ``reload_all`` |
++---------------------------+----------------------------+
.. _default_locale_name_setting:
Default Locale Name
---------------------
+-------------------
-The value supplied here is used as the default locale name when a
-:term:`locale negotiator` is not registered.
+The value supplied here is used as the default locale name when a :term:`locale
+negotiator` is not registered.
.. seealso::
@@ -224,8 +205,6 @@ The value supplied here is used as the default locale name when a
+=================================+===================================+
| ``PYRAMID_DEFAULT_LOCALE_NAME`` | ``pyramid.default_locale_name`` |
| | or ``default_locale_name`` |
-| | |
-| | |
+---------------------------------+-----------------------------------+
.. _including_packages:
@@ -235,19 +214,16 @@ Including Packages
``pyramid.includes`` instructs your application to include other packages.
Using the setting is equivalent to using the
-:meth:`pyramid.config.Configurator.include` method.
+:meth:`pyramid.config.Configurator.include` method.
-+---------------------------------+
-| Config File Setting Name |
-+=================================+
-| ``pyramid.includes`` |
-| |
-| |
-| |
-+---------------------------------+
++--------------------------+
+| Config File Setting Name |
++==========================+
+| ``pyramid.includes`` |
++--------------------------+
-The value assigned to ``pyramid.includes`` should be a sequence. The
-sequence can take several different forms.
+The value assigned to ``pyramid.includes`` should be a sequence. The sequence
+can take several different forms.
1) It can be a string.
@@ -306,7 +282,7 @@ Plain Python
++++++++++++
Using the following ``pyramid.includes`` setting in your plain-Python Pyramid
-application:
+application:
.. code-block:: python
:linenos:
@@ -341,33 +317,29 @@ This value allows you to perform explicit :term:`tween` ordering in your
configuration. Tweens are bits of code used by add-on authors to extend
Pyramid. They form a chain, and require ordering.
-Ideally, you won't need to use the ``pyramid.tweens`` setting at all. Tweens
+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.Configurator.include`.
Authors of included add-ons provide "implicit" tween configuration ordering
-hints to Pyramid when their packages are included. However, the implicit
-tween ordering is only best-effort. Pyramid will attempt to provide an
-implicit order of tweens as best it can using hints provided by add-on
-authors, but because it's only best-effort, if very precise tween ordering is
-required, the only surefire way to get it is to use an explicit tween order.
-You may be required to inspect your tween ordering (see
-:ref:`displaying_tweens`) and add a ``pyramid.tweens`` configuration value at
-the behest of an add-on author.
-
-+---------------------------------+
-| Config File Setting Name |
-+=================================+
-| ``pyramid.tweens`` |
-| |
-| |
-| |
-+---------------------------------+
-
-The value assigned to ``pyramid.tweens`` should be a sequence. The
-sequence can take several different forms.
+hints to Pyramid when their packages are included. However, the implicit tween
+ordering is only best-effort. Pyramid will attempt to provide an implicit
+order of tweens as best it can using hints provided by add-on authors, but
+because it's only best-effort, if very precise tween ordering is required, the
+only surefire way to get it is to use an explicit tween order. You may be
+required to inspect your tween ordering (see :ref:`displaying_tweens`) and add
+a ``pyramid.tweens`` configuration value at the behest of an add-on author.
+
++---------------------------+
+| Config File Setting Name |
++===========================+
+| ``pyramid.tweens`` |
++---------------------------+
+
+The value assigned to ``pyramid.tweens`` should be a sequence. The sequence
+can take several different forms.
1) It can be a string.
@@ -375,11 +347,11 @@ sequence can take several different forms.
pkg.tween_factory1 pkg.tween_factory2 pkg.tween_factory3
- The tween names can also be separated by carriage returns::
+ The tween names can also be separated by carriage returns::
- pkg.tween_factory1
- pkg.tween_factory2
- pkg.tween_factory3
+ pkg.tween_factory1
+ pkg.tween_factory2
+ pkg.tween_factory3
2) It can be a Python list, where the values are strings::
@@ -390,8 +362,8 @@ Each value in the sequence should be a :term:`dotted Python name`.
PasteDeploy Configuration vs. Plain-Python Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Using the following ``pyramid.tweens`` setting in the PasteDeploy ``.ini``
-file in your application:
+Using the following ``pyramid.tweens`` setting in the PasteDeploy ``.ini`` file
+in your application:
.. code-block:: ini
@@ -420,12 +392,11 @@ It is fine to use both or either form.
Examples
--------
-Let's presume your configuration file is named ``MyProject.ini``, and
-there is a section representing your application named ``[app:main]``
-within the file that represents your :app:`Pyramid` application.
-The configuration file settings documented in the above "Config File
-Setting Name" column would go in the ``[app:main]`` section. Here's
-an example of such a section:
+Let's presume your configuration file is named ``MyProject.ini``, and there is
+a section representing your application named ``[app:main]`` within the file
+that represents your :app:`Pyramid` application. The configuration file
+settings documented in the above "Config File Setting Name" column would go in
+the ``[app:main]`` section. Here's an example of such a section:
.. code-block:: ini
:linenos:
@@ -435,41 +406,39 @@ an example of such a section:
pyramid.reload_templates = true
pyramid.debug_authorization = true
-You can also use environment variables to accomplish the same purpose
-for settings documented as such. For example, you might start your
-:app:`Pyramid` application using the following command line:
+You can also use environment variables to accomplish the same purpose for
+settings documented as such. For example, you might start your :app:`Pyramid`
+application using the following command line:
.. code-block:: text
$ PYRAMID_DEBUG_AUTHORIZATION=1 PYRAMID_RELOAD_TEMPLATES=1 \
$VENV/bin/pserve MyProject.ini
-If you started your application this way, your :app:`Pyramid`
-application would behave in the same manner as if you had placed the
-respective settings in the ``[app:main]`` section of your
-application's ``.ini`` file.
+If you started your application this way, your :app:`Pyramid` application would
+behave in the same manner as if you had placed the respective settings in the
+``[app:main]`` section of your application's ``.ini`` file.
-If you want to turn all ``debug`` settings (every setting that starts
-with ``pyramid.debug_``). on in one fell swoop, you can use
-``PYRAMID_DEBUG_ALL=1`` as an environment variable setting or you may use
-``pyramid.debug_all=true`` in the config file. Note that this does not affect
-settings that do not start with ``pyramid.debug_*`` such as
-``pyramid.reload_templates``.
+If you want to turn all ``debug`` settings (every setting that starts with
+``pyramid.debug_``) on in one fell swoop, you can use ``PYRAMID_DEBUG_ALL=1``
+as an environment variable setting or you may use ``pyramid.debug_all=true`` in
+the config file. Note that this does not affect settings that do not start
+with ``pyramid.debug_*`` such as ``pyramid.reload_templates``.
If you want to turn all ``pyramid.reload`` settings (every setting that starts
with ``pyramid.reload_``) on in one fell swoop, you can use
``PYRAMID_RELOAD_ALL=1`` as an environment variable setting or you may use
-``pyramid.reload_all=true`` in the config file. Note that this does not
-affect settings that do not start with ``pyramid.reload_*`` such as
+``pyramid.reload_all=true`` in the config file. Note that this does not affect
+settings that do not start with ``pyramid.reload_*`` such as
``pyramid.debug_notfound``.
.. note::
Specifying configuration settings via environment variables is generally
- most useful during development, where you may wish to augment or
- override the more permanent settings in the configuration file.
- This is useful because many of the reload and debug settings may
- have performance or security (i.e., disclosure) implications
- that make them undesirable in a production environment.
+ most useful during development, where you may wish to augment or override
+ the more permanent settings in the configuration file. This is useful
+ because many of the reload and debug settings may have performance or
+ security (i.e., disclosure) implications that make them undesirable in a
+ production environment.
.. index::
single: reload_templates
@@ -480,13 +449,13 @@ Understanding the Distinction Between ``reload_templates`` and ``reload_assets``
The difference between ``pyramid.reload_assets`` and
``pyramid.reload_templates`` is a bit subtle. Templates are themselves also
-treated by :app:`Pyramid` as asset files (along with other static files), so the
-distinction can be confusing. It's helpful to read
+treated by :app:`Pyramid` as asset files (along with other static files), so
+the distinction can be confusing. It's helpful to read
:ref:`overriding_assets_section` for some context about assets in general.
-When ``pyramid.reload_templates`` is true, :app:`Pyramid` takes advantage of the
-underlying templating systems' ability to check for file modifications to an
-individual template file. When ``pyramid.reload_templates`` is true but
+When ``pyramid.reload_templates`` is true, :app:`Pyramid` takes advantage of
+the underlying templating system's ability to check for file modifications to
+an individual template file. When ``pyramid.reload_templates`` is true, but
``pyramid.reload_assets`` is *not* true, the template filename returned by the
``pkg_resources`` package (used under the hood by asset resolution) is cached
by :app:`Pyramid` on the first request. Subsequent requests for the same
@@ -499,21 +468,21 @@ because it has some effect).
However, when ``pyramid.reload_assets`` is true, :app:`Pyramid` will not cache
the template filename, meaning you can see the effect of changing the content
of an overridden asset directory for templates without restarting the server
-after every change. Subsequent requests for the same template file may
-return different filenames based on the current state of overridden asset
-directories. Setting ``pyramid.reload_assets`` to ``True`` affects performance
-*dramatically*, slowing things down by an order of magnitude for each
-template rendering. However, it's convenient to enable when moving files
-around in overridden asset directories. ``pyramid.reload_assets`` makes the
-system *very slow* when templates are in use. Never set
-``pyramid.reload_assets`` to ``True`` on a production system.
+after every change. Subsequent requests for the same template file may return
+different filenames based on the current state of overridden asset directories.
+Setting ``pyramid.reload_assets`` to ``True`` affects performance
+*dramatically*, slowing things down by an order of magnitude for each template
+rendering. However, it's convenient to enable when moving files around in
+overridden asset directories. ``pyramid.reload_assets`` makes the system *very
+slow* when templates are in use. Never set ``pyramid.reload_assets`` to
+``True`` on a production system.
.. index::
par: settings; adding custom
.. _adding_a_custom_setting:
-Adding A Custom Setting
+Adding a Custom Setting
-----------------------
From time to time, you may need to add a custom setting to your application.
@@ -530,12 +499,12 @@ Here's how:
debug_frobnosticator = True
- In the ``main()`` function that represents the place that your Pyramid WSGI
- application is created, anticipate that you'll be getting this key/value
- pair as a setting and do any type conversion necessary.
+ application is created, anticipate that you'll be getting this key/value pair
+ as a setting and do any type conversion necessary.
- If you've done any type conversion of your custom value, reset the
- converted values into the ``settings`` dictionary *before* you pass the
- dictionary as ``settings`` to the :term:`Configurator`. For example:
+ If you've done any type conversion of your custom value, reset the converted
+ values into the ``settings`` dictionary *before* you pass the dictionary as
+ ``settings`` to the :term:`Configurator`. For example:
.. code-block:: python
@@ -547,16 +516,17 @@ Here's how:
settings['debug_frobnosticator'] = debug_frobnosticator
config = Configurator(settings=settings)
- .. note:: It's especially important that you mutate the ``settings``
- dictionary with the converted version of the variable *before* passing
- it to the Configurator: the configurator makes a *copy* of ``settings``,
- it doesn't use the one you pass directly.
-
-- When creating an ``includeme`` function that will be later added to your
+ .. note::
+ It's especially important that you mutate the ``settings`` dictionary with
+ the converted version of the variable *before* passing it to the
+ Configurator: the configurator makes a *copy* of ``settings``, it doesn't
+ use the one you pass directly.
+
+- When creating an ``includeme`` function that will be later added to your
application's configuration you may access the ``settings`` dictionary
through the instance of the :term:`Configurator` that is passed into the
function as its only argument. For Example:
-
+
.. code-block:: python
def includeme(config):
@@ -573,8 +543,8 @@ Here's how:
settings = request.registry.settings
debug_frobnosticator = settings['debug_frobnosticator']
- If you wish to use the value in code that does not have access to the
- request and you wish to use the value, you'll need to use the
+ If you wish to use the value in code that does not have access to the request
+ and you wish to use the value, you'll need to use the
:func:`pyramid.threadlocal.get_current_registry` API to obtain the current
registry, then ask for its ``settings`` attribute. For example:
@@ -583,7 +553,3 @@ Here's how:
registry = pyramid.threadlocal.get_current_registry()
settings = registry.settings
debug_frobnosticator = settings['debug_frobnosticator']
-
-
-
-
diff --git a/docs/narr/events.rst b/docs/narr/events.rst
index 09caac898..c10d4cc47 100644
--- a/docs/narr/events.rst
+++ b/docs/narr/events.rst
@@ -9,18 +9,18 @@
.. _events_chapter:
Using Events
-=============
+============
-An *event* is an object broadcast by the :app:`Pyramid` framework
-at interesting points during the lifetime of an application. You
-don't need to use events in order to create most :app:`Pyramid`
-applications, but they can be useful when you want to perform slightly
-advanced operations. For example, subscribing to an event can allow
-you to run some code as the result of every new request.
+An *event* is an object broadcast by the :app:`Pyramid` framework at
+interesting points during the lifetime of an application. You don't need to
+use events in order to create most :app:`Pyramid` applications, but they can be
+useful when you want to perform slightly advanced operations. For example,
+subscribing to an event can allow you to run some code as the result of every
+new request.
-Events in :app:`Pyramid` are always broadcast by the framework.
-However, they only become useful when you register a *subscriber*. A
-subscriber is a function that accepts a single argument named `event`:
+Events in :app:`Pyramid` are always broadcast by the framework. However, they
+only become useful when you register a *subscriber*. A subscriber is a
+function that accepts a single argument named `event`:
.. code-block:: python
:linenos:
@@ -28,23 +28,20 @@ subscriber is a function that accepts a single argument named `event`:
def mysubscriber(event):
print(event)
-The above is a subscriber that simply prints the event to the console
-when it's called.
+The above is a subscriber that simply prints the event to the console when it's
+called.
The mere existence of a subscriber function, however, is not sufficient to
arrange for it to be called. To arrange for the subscriber to be called,
-you'll need to use the
-:meth:`pyramid.config.Configurator.add_subscriber` method or you'll
-need to use the :func:`pyramid.events.subscriber` decorator to decorate a
-function found via a :term:`scan`.
+you'll need to use the :meth:`pyramid.config.Configurator.add_subscriber`
+method or you'll need to use the :func:`pyramid.events.subscriber` decorator to
+decorate a function found via a :term:`scan`.
Configuring an Event Listener Imperatively
------------------------------------------
-You can imperatively configure a subscriber function to be called
-for some event type via the
-:meth:`~pyramid.config.Configurator.add_subscriber`
-method:
+You can imperatively configure a subscriber function to be called for some
+event type via the :meth:`~pyramid.config.Configurator.add_subscriber` method:
.. code-block:: python
:linenos:
@@ -58,10 +55,9 @@ method:
config.add_subscriber(mysubscriber, NewRequest)
-The first argument to
-:meth:`~pyramid.config.Configurator.add_subscriber` is the
-subscriber function (or a :term:`dotted Python name` which refers
-to a subscriber callable); the second argument is the event type.
+The first argument to :meth:`~pyramid.config.Configurator.add_subscriber` is
+the subscriber function (or a :term:`dotted Python name` which refers to a
+subscriber callable); the second argument is the event type.
.. seealso::
@@ -70,8 +66,8 @@ to a subscriber callable); the second argument is the event type.
Configuring an Event Listener Using a Decorator
-----------------------------------------------
-You can configure a subscriber function to be called for some event
-type via the :func:`pyramid.events.subscriber` function.
+You can configure a subscriber function to be called for some event type via
+the :func:`pyramid.events.subscriber` function.
.. code-block:: python
:linenos:
@@ -83,9 +79,9 @@ type via the :func:`pyramid.events.subscriber` function.
def mysubscriber(event):
event.request.foo = 1
-When the :func:`~pyramid.events.subscriber` decorator is used a
-:term:`scan` must be performed against the package containing the
-decorated function for the decorator to have any effect.
+When the :func:`~pyramid.events.subscriber` decorator is used, a :term:`scan`
+must be performed against the package containing the decorated function for the
+decorator to have any effect.
Either of the above registration examples implies that every time the
:app:`Pyramid` framework emits an event object that supplies an
@@ -96,13 +92,12 @@ As you can see, a subscription is made in terms of a *class* (such as
:class:`pyramid.events.NewResponse`). The event object sent to a subscriber
will always be an object that possesses an :term:`interface`. For
:class:`pyramid.events.NewResponse`, that interface is
-:class:`pyramid.interfaces.INewResponse`. The interface documentation
-provides information about available attributes and methods of the event
-objects.
+:class:`pyramid.interfaces.INewResponse`. The interface documentation provides
+information about available attributes and methods of the event objects.
-The return value of a subscriber function is ignored. Subscribers to
-the same event type are not guaranteed to be called in any particular
-order relative to each other.
+The return value of a subscriber function is ignored. Subscribers to the same
+event type are not guaranteed to be called in any particular order relative to
+each other.
All the concrete :app:`Pyramid` event types are documented in the
:ref:`events_module` API documentation.
@@ -110,8 +105,8 @@ All the concrete :app:`Pyramid` event types are documented in the
An Example
----------
-If you create event listener functions in a ``subscribers.py`` file in
-your application like so:
+If you create event listener functions in a ``subscribers.py`` file in your
+application like so:
.. code-block:: python
:linenos:
@@ -122,9 +117,8 @@ your application like so:
def handle_new_response(event):
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
-configuration startup:
+You may configure these functions to be called at the appropriate times by
+adding the following code to your application's configuration startup:
.. code-block:: python
:linenos:
@@ -136,22 +130,21 @@ configuration startup:
config.add_subscriber('myproject.subscribers.handle_new_response',
'pyramid.events.NewResponse')
-Either mechanism causes the functions in ``subscribers.py`` to be
-registered as event subscribers. Under this configuration, when the
-application is run, each time a new request or response is detected, a
-message will be printed to the console.
+Either mechanism causes the functions in ``subscribers.py`` to be registered as
+event subscribers. Under this configuration, when the application is run, each
+time a new request or response is detected, a message will be printed to the
+console.
-Each of our subscriber functions accepts an ``event`` object and
-prints an attribute of the event object. This begs the question: how
-can we know which attributes a particular event has?
+Each of our subscriber functions accepts an ``event`` object and prints an
+attribute of the event object. This begs the question: how can we know which
+attributes a particular event has?
We know that :class:`pyramid.events.NewRequest` event objects have a
-``request`` attribute, which is a :term:`request` object, because the
-interface defined at :class:`pyramid.interfaces.INewRequest` says it must.
-Likewise, we know that :class:`pyramid.interfaces.NewResponse` events have a
-``response`` attribute, which is a response object constructed by your
-application, because the interface defined at
-:class:`pyramid.interfaces.INewResponse` says it must
+``request`` attribute, which is a :term:`request` object, because the interface
+defined at :class:`pyramid.interfaces.INewRequest` says it must. Likewise, we
+know that :class:`pyramid.interfaces.NewResponse` events have a ``response``
+attribute, which is a response object constructed by your application, because
+the interface defined at :class:`pyramid.interfaces.INewResponse` says it must
(:class:`pyramid.events.NewResponse` objects also have a ``request``).
.. _custom_events:
@@ -159,21 +152,20 @@ application, because the interface defined at
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.
+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.
+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:
+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:
@@ -193,11 +185,10 @@ example custom event classes:
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:
+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:
@@ -211,12 +202,12 @@ event with a decorator:
# 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.
+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:
+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:
@@ -229,11 +220,10 @@ accessed as ``request.registry.notify``. For example:
request.registry.notify(event)
return {'document': doc}
-This example view will notify all subscribers to the custom
-``DocCreated`` event.
+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.
+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/firstapp.rst b/docs/narr/firstapp.rst
index e73ef66ac..6a952dec9 100644
--- a/docs/narr/firstapp.rst
+++ b/docs/narr/firstapp.rst
@@ -4,7 +4,7 @@
.. _firstapp_chapter:
Creating Your First :app:`Pyramid` Application
-=================================================
+==============================================
In this chapter, we will walk through the creation of a tiny :app:`Pyramid`
application. After we're finished creating the application, we'll explain in
@@ -37,28 +37,27 @@ On Windows:
C:\> %VENV%\Scripts\python.exe helloworld.py
-This command will not return and nothing will be printed to the console.
-When port 8080 is visited by a browser on the URL ``/hello/world``, the
-server will simply serve up the text "Hello world!". If your application is
-running on your local system, using `<http://localhost:8080/hello/world>`_
-in a browser will show this result.
+This command will not return and nothing will be printed to the console. When
+port 8080 is visited by a browser on the URL ``/hello/world``, the server will
+simply serve up the text "Hello world!". If your application is running on
+your local system, using `<http://localhost:8080/hello/world>`_ in a browser
+will show this result.
Each time you visit a URL served by the application in a browser, a logging
line will be emitted to the console displaying the hostname, the date, the
-request method and path, and some additional information. This output is
-done by the wsgiref server we've used to serve this application. It logs an
-"access log" in Apache combined logging format to the console.
+request method and path, and some additional information. This output is done
+by the wsgiref server we've used to serve this application. It logs an "access
+log" in Apache combined logging format to the console.
Press ``Ctrl-C`` (or ``Ctrl-Break`` on Windows) to stop the application.
Now that we have a rudimentary understanding of what the application does,
-let's examine it piece-by-piece.
+let's examine it piece by piece.
Imports
~~~~~~~
-The above ``helloworld.py`` script uses the following set of import
-statements:
+The above ``helloworld.py`` script uses the following set of import statements:
.. literalinclude:: helloworld.py
:linenos:
@@ -71,32 +70,32 @@ The script imports the :class:`~pyramid.config.Configurator` class from the
Like many other Python web frameworks, :app:`Pyramid` uses the :term:`WSGI`
protocol to connect an application and a web server together. The
-:mod:`wsgiref` server is used in this example as a WSGI server for
-convenience, as it is shipped within the Python standard library.
+:mod:`wsgiref` server is used in this example as a WSGI server for convenience,
+as it is shipped within the Python standard library.
-The script also imports the :class:`pyramid.response.Response` class for
-later use. An instance of this class will be used to create a web response.
+The script also imports the :class:`pyramid.response.Response` class for later
+use. An instance of this class will be used to create a web response.
View Callable Declarations
~~~~~~~~~~~~~~~~~~~~~~~~~~
-The above script, beneath its set of imports, defines a function
-named ``hello_world``.
+The above script, beneath its set of imports, defines a function named
+``hello_world``.
.. literalinclude:: helloworld.py
:linenos:
:pyobject: hello_world
-The function accepts a single argument (``request``) and it returns an
-instance of the :class:`pyramid.response.Response` class. The single
-argument to the class' constructor is a string computed from parameters
-matched from the URL. This value becomes the body of the response.
+The function accepts a single argument (``request``) and it returns an instance
+of the :class:`pyramid.response.Response` class. The single argument to the
+class' constructor is a string computed from parameters matched from the URL.
+This value becomes the body of the response.
-This function is known as a :term:`view callable`. A view callable
-accepts a single argument, ``request``. It is expected to return a
-:term:`response` object. A view callable doesn't need to be a function; it
-can be represented via another type of object, like a class or an instance,
-but for our purposes here, a function serves us well.
+This function is known as a :term:`view callable`. A view callable accepts a
+single argument, ``request``. It is expected to return a :term:`response`
+object. A view callable doesn't need to be a function; it can be represented
+via another type of object, like a class or an instance, but for our purposes
+here, a function serves us well.
A view callable is always called with a :term:`request` object. A request
object is a representation of an HTTP request sent to :app:`Pyramid` via the
@@ -105,10 +104,10 @@ active :term:`WSGI` server.
A view callable is required to return a :term:`response` object because a
response object has all the information necessary to formulate an actual HTTP
response; this object is then converted to text by the :term:`WSGI` server
-which called Pyramid and it is sent back to the requesting browser. To
-return a response, each view callable creates an instance of the
-:class:`~pyramid.response.Response` class. In the ``hello_world`` function,
-a string is passed as the body to the response.
+which called Pyramid and it is sent back to the requesting browser. To return
+a response, each view callable creates an instance of the
+:class:`~pyramid.response.Response` class. In the ``hello_world`` function, a
+string is passed as the body to the response.
.. index::
single: imperative configuration
@@ -120,16 +119,16 @@ a string is passed as the body to the response.
Application Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~
-In the above script, the following code represents the *configuration* of
-this simple application. The application is configured using the previously
-defined imports and function definitions, placed within the confines of an
-``if`` statement:
+In the above script, the following code represents the *configuration* of this
+simple application. The application is configured using the previously defined
+imports and function definitions, placed within the confines of an ``if``
+statement:
.. literalinclude:: helloworld.py
:linenos:
:lines: 9-15
-Let's break this down piece-by-piece.
+Let's break this down piece by piece.
Configurator Construction
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -140,26 +139,26 @@ Configurator Construction
The ``if __name__ == '__main__':`` line in the code sample above represents a
Python idiom: the code inside this if clause is not invoked unless the script
-containing this code is run directly from the operating system command
-line. For example, if the file named ``helloworld.py`` contains the entire
-script body, the code within the ``if`` statement will only be invoked when
-``python helloworld.py`` is executed from the command line.
-
-Using the ``if`` clause is necessary -- or at least best practice -- because
-code in a Python ``.py`` file may be eventually imported via the Python
-``import`` statement by another ``.py`` file. ``.py`` files that are
-imported by other ``.py`` files are referred to as *modules*. By using the
-``if __name__ == '__main__':`` idiom, the script above is indicating that it does
-not want the code within the ``if`` statement to execute if this module is
-imported from another; the code within the ``if`` block should only be run
-during a direct script execution.
+containing this code is run directly from the operating system command line.
+For example, if the file named ``helloworld.py`` contains the entire script
+body, the code within the ``if`` statement will only be invoked when ``python
+helloworld.py`` is executed from the command line.
+
+Using the ``if`` clause is necessary—or at least best practice—because code in
+a Python ``.py`` file may be eventually imported via the Python ``import``
+statement by another ``.py`` file. ``.py`` files that are imported by other
+``.py`` files are referred to as *modules*. By using the ``if __name__ ==
+'__main__':`` idiom, the script above is indicating that it does not want the
+code within the ``if`` statement to execute if this module is imported from
+another; the code within the ``if`` block should only be run during a direct
+script execution.
The ``config = Configurator()`` line above creates an instance of the
:class:`~pyramid.config.Configurator` class. The resulting ``config`` object
represents an API which the script uses to configure this particular
:app:`Pyramid` application. Methods called on the Configurator will cause
-registrations to be made in an :term:`application registry` associated with
-the application.
+registrations to be made in an :term:`application registry` associated with the
+application.
.. _adding_configuration:
@@ -170,13 +169,13 @@ Adding Configuration
:linenos:
:lines: 11-12
-First line above calls the :meth:`pyramid.config.Configurator.add_route`
-method, which registers a :term:`route` to match any URL path that begins
-with ``/hello/`` followed by a string.
+The first line above calls the :meth:`pyramid.config.Configurator.add_route`
+method, which registers a :term:`route` to match any URL path that begins with
+``/hello/`` followed by a string.
-The second line registers the ``hello_world`` function as a
-:term:`view callable` and makes sure that it will be called when the
-``hello`` route is matched.
+The second line registers the ``hello_world`` function as a :term:`view
+callable` and makes sure that it will be called when the ``hello`` route is
+matched.
.. index::
single: make_wsgi_app
@@ -190,25 +189,24 @@ WSGI Application Creation
:lines: 13
After configuring views and ending configuration, the script creates a WSGI
-*application* via the :meth:`pyramid.config.Configurator.make_wsgi_app`
-method. A call to ``make_wsgi_app`` implies that all configuration is
-finished (meaning all method calls to the configurator, which sets up views
-and various other configuration settings, have been performed). The
-``make_wsgi_app`` method returns a :term:`WSGI` application object that can
-be used by any WSGI server to present an application to a requestor.
-:term:`WSGI` is a protocol that allows servers to talk to Python
-applications. We don't discuss :term:`WSGI` in any depth within this book,
-but you can learn more about it by visiting `wsgi.org
-<http://wsgi.org>`_.
-
-The :app:`Pyramid` application object, in particular, is an instance of a
-class representing a :app:`Pyramid` :term:`router`. It has a reference to
-the :term:`application registry` which resulted from method calls to the
-configurator used to configure it. The :term:`router` consults the registry
-to obey the policy choices made by a single application. These policy
-choices were informed by method calls to the :term:`Configurator` made
-earlier; in our case, the only policy choices made were implied by calls
-to its ``add_view`` and ``add_route`` methods.
+*application* via the :meth:`pyramid.config.Configurator.make_wsgi_app` method.
+A call to ``make_wsgi_app`` implies that all configuration is finished
+(meaning all method calls to the configurator, which sets up views and various
+other configuration settings, have been performed). The ``make_wsgi_app``
+method returns a :term:`WSGI` application object that can be used by any WSGI
+server to present an application to a requestor. :term:`WSGI` is a protocol
+that allows servers to talk to Python applications. We don't discuss
+:term:`WSGI` in any depth within this book, but you can learn more about it by
+visiting `wsgi.org <http://wsgi.org>`_.
+
+The :app:`Pyramid` application object, in particular, is an instance of a class
+representing a :app:`Pyramid` :term:`router`. It has a reference to the
+:term:`application registry` which resulted from method calls to the
+configurator used to configure it. The :term:`router` consults the registry to
+obey the policy choices made by a single application. These policy choices
+were informed by method calls to the :term:`Configurator` made earlier; in our
+case, the only policy choices made were implied by calls to its ``add_view``
+and ``add_route`` methods.
WSGI Application Serving
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -217,37 +215,36 @@ WSGI Application Serving
:linenos:
:lines: 14-15
-Finally, we actually serve the application to requestors by starting up a
-WSGI server. We happen to use the :mod:`wsgiref` ``make_server`` server
-maker for this purpose. We pass in as the first argument ``'0.0.0.0'``,
-which means "listen on all TCP interfaces." By default, the HTTP server
-listens only on the ``127.0.0.1`` interface, which is problematic if you're
-running the server on a remote system and you wish to access it with a web
-browser from a local system. We also specify a TCP port number to listen on,
-which is 8080, passing it as the second argument. The final argument is the
-``app`` object (a :term:`router`), which is the application we wish to
-serve. Finally, we call the server's ``serve_forever`` method, which starts
-the main loop in which it will wait for requests from the outside world.
-
-When this line is invoked, it causes the server to start listening on TCP
-port 8080. The server will serve requests forever, or at least until we stop
-it by killing the process which runs it (usually by pressing ``Ctrl-C``
-or ``Ctrl-Break`` in the terminal we used to start it).
+Finally, we actually serve the application to requestors by starting up a WSGI
+server. We happen to use the :mod:`wsgiref` ``make_server`` server maker for
+this purpose. We pass in as the first argument ``'0.0.0.0'``, which means
+"listen on all TCP interfaces". By default, the HTTP server listens only on
+the ``127.0.0.1`` interface, which is problematic if you're running the server
+on a remote system and you wish to access it with a web browser from a local
+system. We also specify a TCP port number to listen on, which is 8080, passing
+it as the second argument. The final argument is the ``app`` object (a
+:term:`router`), which is the application we wish to serve. Finally, we call
+the server's ``serve_forever`` method, which starts the main loop in which it
+will wait for requests from the outside world.
+
+When this line is invoked, it causes the server to start listening on TCP port
+8080. The server will serve requests forever, or at least until we stop it by
+killing the process which runs it (usually by pressing ``Ctrl-C`` or
+``Ctrl-Break`` in the terminal we used to start it).
Conclusion
~~~~~~~~~~
Our hello world application is one of the simplest possible :app:`Pyramid`
applications, configured "imperatively". We can see that it's configured
-imperatively because the full power of Python is available to us as we
-perform configuration tasks.
+imperatively because the full power of Python is available to us as we perform
+configuration tasks.
References
----------
-For more information about the API of a :term:`Configurator` object,
-see :class:`~pyramid.config.Configurator` .
+For more information about the API of a :term:`Configurator` object, see
+:class:`~pyramid.config.Configurator` .
For more information about :term:`view configuration`, see
:ref:`view_config_chapter`.
-
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index 3c804a158..bb0bbe511 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -9,16 +9,16 @@
Internationalization and Localization
=====================================
-:term:`Internationalization` (i18n) is the act of creating software
-with a user interface that can potentially be displayed in more than
-one language or cultural context. :term:`Localization` (l10n) is the
-process of displaying the user interface of an internationalized
-application in a *particular* language or cultural context.
+:term:`Internationalization` (i18n) is the act of creating software with a user
+interface that can potentially be displayed in more than one language or
+cultural context. :term:`Localization` (l10n) is the process of displaying the
+user interface of an internationalized application in a *particular* language
+or cultural context.
-:app:`Pyramid` offers internationalization and localization
-subsystems that can be used to translate the text of buttons, error
-messages and other software- and template-defined values into the
-native language of a user of your application.
+:app:`Pyramid` offers internationalization and localization subsystems that can
+be used to translate the text of buttons, error messages, and other software-
+and template-defined values into the native language of a user of your
+application.
.. index::
single: translation string
@@ -29,15 +29,15 @@ native language of a user of your application.
Creating a Translation String
-----------------------------
-While you write your software, you can insert specialized markup into
-your Python code that makes it possible for the system to translate
-text values into the languages used by your application's users. This
-markup creates a :term:`translation string`. A translation string is
-an object that behaves mostly like a normal Unicode object, except that
-it also carries around extra information related to its job as part of
-the :app:`Pyramid` translation machinery.
+While you write your software, you can insert specialized markup into your
+Python code that makes it possible for the system to translate text values into
+the languages used by your application's users. This markup creates a
+:term:`translation string`. A translation string is an object that behaves
+mostly like a normal Unicode object, except that it also carries around extra
+information related to its job as part of the :app:`Pyramid` translation
+machinery.
-Using The ``TranslationString`` Class
+Using the ``TranslationString`` Class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The most primitive way to create a translation string is to use the
@@ -53,17 +53,17 @@ This creates a Unicode-like object that is a TranslationString.
.. note::
- For people more familiar with :term:`Zope` i18n, a TranslationString
- is a lot like a ``zope.i18nmessageid.Message`` object. It is not a
- subclass, however. For people more familiar with :term:`Pylons` or
- :term:`Django` i18n, using a TranslationString is a lot like using
- "lazy" versions of related gettext APIs.
+ For people more familiar with :term:`Zope` i18n, a TranslationString is a
+ lot like a ``zope.i18nmessageid.Message`` object. It is not a subclass,
+ however. For people more familiar with :term:`Pylons` or :term:`Django`
+ i18n, using a TranslationString is a lot like using "lazy" versions of
+ related gettext APIs.
-The first argument to :class:`~pyramid.i18n.TranslationString` is
-the ``msgid``; it is required. It represents the key into the
-translation mappings provided by a particular localization. The
-``msgid`` argument must be a Unicode object or an ASCII string. The
-msgid may optionally contain *replacement markers*. For instance:
+The first argument to :class:`~pyramid.i18n.TranslationString` is the
+``msgid``; it is required. It represents the key into the translation mappings
+provided by a particular localization. The ``msgid`` argument must be a Unicode
+object or an ASCII string. The msgid may optionally contain *replacement
+markers*. For instance:
.. code-block:: python
:linenos:
@@ -71,10 +71,9 @@ msgid may optionally contain *replacement markers*. For instance:
from pyramid.i18n import TranslationString
ts = TranslationString('Add ${number}')
-Within the string above, ``${number}`` is a replacement marker. It
-will be replaced by whatever is in the *mapping* for a translation
-string. The mapping may be supplied at the same time as the
-replacement marker itself:
+Within the string above, ``${number}`` is a replacement marker. It will be
+replaced by whatever is in the *mapping* for a translation string. The mapping
+may be supplied at the same time as the replacement marker itself:
.. code-block:: python
:linenos:
@@ -82,14 +81,14 @@ replacement marker itself:
from pyramid.i18n import TranslationString
ts = TranslationString('Add ${number}', mapping={'number':1})
-Any number of replacement markers can be present in the msgid value,
-any number of times. Only markers which can be replaced by the values
-in the *mapping* will be replaced at translation time. The others
-will not be interpolated and will be output literally.
+Any number of replacement markers can be present in the msgid value, any number
+of times. Only markers which can be replaced by the values in the *mapping*
+will be replaced at translation time. The others will not be interpolated and
+will be output literally.
A translation string should also usually carry a *domain*. The domain
-represents a translation category to disambiguate it from other
-translations of the same msgid, in case they conflict.
+represents a translation category to disambiguate it from other translations of
+the same msgid, in case they conflict.
.. code-block:: python
:linenos:
@@ -98,13 +97,12 @@ translations of the same msgid, in case they conflict.
ts = TranslationString('Add ${number}', mapping={'number':1},
domain='form')
-The above translation string named a domain of ``form``. A
-:term:`translator` function will often use the domain to locate the
-right translator file on the filesystem which contains translations
-for a given domain. In this case, if it were trying to translate
-our msgid to German, it might try to find a translation from a
-:term:`gettext` file within a :term:`translation directory` like this
-one:
+The above translation string named a domain of ``form``. A :term:`translator`
+function will often use the domain to locate the right translator file on the
+filesystem which contains translations for a given domain. In this case, if it
+were trying to translate our msgid to German, it might try to find a
+translation from a :term:`gettext` file within a :term:`translation directory`
+like this one:
.. code-block:: text
@@ -113,14 +111,13 @@ one:
In other words, it would want to take translations from the ``form.mo``
translation file in the German language.
-Finally, the TranslationString constructor accepts a ``default``
-argument. If a ``default`` argument is supplied, it replaces usages
-of the ``msgid`` as the *default value* for the translation string.
-When ``default`` is ``None``, the ``msgid`` value passed to a
-TranslationString is used as an implicit message identifier. Message
-identifiers are matched with translations in translation files, so it
-is often useful to create translation strings with "opaque" message
-identifiers unrelated to their default text:
+Finally, the TranslationString constructor accepts a ``default`` argument. If
+a ``default`` argument is supplied, it replaces usages of the ``msgid`` as the
+*default value* for the translation string. When ``default`` is ``None``, the
+``msgid`` value passed to a TranslationString is used as an implicit message
+identifier. Message identifiers are matched with translations in translation
+files, so it is often useful to create translation strings with "opaque"
+message identifiers unrelated to their default text:
.. code-block:: python
:linenos:
@@ -129,8 +126,7 @@ identifiers unrelated to their default text:
ts = TranslationString('add-number', default='Add ${number}',
domain='form', mapping={'number':1})
-When default text is used, Default text objects may contain
-replacement values.
+When default text is used, Default text objects may contain replacement values.
.. index::
single: translation string factory
@@ -139,10 +135,10 @@ Using the ``TranslationStringFactory`` Class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Another way to generate a translation string is to use the
-:attr:`~pyramid.i18n.TranslationStringFactory` object. This object
-is a *translation string factory*. Basically a translation string
-factory presets the ``domain`` value of any :term:`translation string`
-generated by using it. For example:
+:attr:`~pyramid.i18n.TranslationStringFactory` object. This object is a
+*translation string factory*. Basically a translation string factory presets
+the ``domain`` value of any :term:`translation string` generated by using it.
+For example:
.. code-block:: python
:linenos:
@@ -151,20 +147,18 @@ generated by using it. For example:
_ = TranslationStringFactory('pyramid')
ts = _('add-number', default='Add ${number}', mapping={'number':1})
-.. note:: We assigned the translation string factory to the name
- ``_``. This is a convention which will be supported by translation
- file generation tools.
+.. note:: We assigned the translation string factory to the name ``_``. This
+ is a convention which will be supported by translation file generation
+ tools.
After assigning ``_`` to the result of a
-:func:`~pyramid.i18n.TranslationStringFactory`, the subsequent result
-of calling ``_`` will be a :class:`~pyramid.i18n.TranslationString`
-instance. Even though a ``domain`` value was not passed to ``_`` (as
-would have been necessary if the
-:class:`~pyramid.i18n.TranslationString` constructor were used instead
-of a translation string factory), the ``domain`` attribute of the
-resulting translation string will be ``pyramid``. As a result, the
-previous code example is completely equivalent (except for spelling)
-to:
+:func:`~pyramid.i18n.TranslationStringFactory`, the subsequent result of
+calling ``_`` will be a :class:`~pyramid.i18n.TranslationString` instance.
+Even though a ``domain`` value was not passed to ``_`` (as would have been
+necessary if the :class:`~pyramid.i18n.TranslationString` constructor were used
+instead of a translation string factory), the ``domain`` attribute of the
+resulting translation string will be ``pyramid``. As a result, the previous
+code example is completely equivalent (except for spelling) to:
.. code-block:: python
:linenos:
@@ -173,12 +167,11 @@ to:
ts = _('add-number', default='Add ${number}', mapping={'number':1},
domain='pyramid')
-You can set up your own translation string factory much like the one
-provided above by using the
-:class:`~pyramid.i18n.TranslationStringFactory` class. For example,
-if you'd like to create a translation string factory which presets the
-``domain`` value of generated translation strings to ``form``, you'd
-do something like this:
+You can set up your own translation string factory much like the one provided
+above by using the :class:`~pyramid.i18n.TranslationStringFactory` class. For
+example, if you'd like to create a translation string factory which presets the
+``domain`` value of generated translation strings to ``form``, you'd do
+something like this:
.. code-block:: python
:linenos:
@@ -187,72 +180,68 @@ do something like this:
_ = TranslationStringFactory('form')
ts = _('add-number', default='Add ${number}', mapping={'number':1})
-Creating a unique domain for your application via a translation string
-factory is best practice. Using your own unique translation domain
-allows another person to reuse your application without needing to
-merge your translation files with his own. Instead, he can just
-include your package's :term:`translation directory` via the
-:meth:`pyramid.config.Configurator.add_translation_dirs`
-method.
+Creating a unique domain for your application via a translation string factory
+is best practice. Using your own unique translation domain allows another
+person to reuse your application without needing to merge your translation
+files with their own. Instead they can just include your package's
+:term:`translation directory` via the
+:meth:`pyramid.config.Configurator.add_translation_dirs` method.
.. note::
For people familiar with Zope internationalization, a
TranslationStringFactory is a lot like a
- ``zope.i18nmessageid.MessageFactory`` object. It is not a
- subclass, however.
+ ``zope.i18nmessageid.MessageFactory`` object. It is not a subclass,
+ however.
.. index::
single: gettext
single: translation directories
-Working With ``gettext`` Translation Files
+Working with ``gettext`` Translation Files
------------------------------------------
-The basis of :app:`Pyramid` translation services is
-GNU :term:`gettext`. Once your application source code files and templates
-are marked up with translation markers, you can work on translations
-by creating various kinds of gettext files.
+The basis of :app:`Pyramid` translation services is GNU :term:`gettext`. Once
+your application source code files and templates are marked up with translation
+markers, you can work on translations by creating various kinds of gettext
+files.
.. note::
- The steps a developer must take to work with :term:`gettext`
- :term:`message catalog` files within a :app:`Pyramid`
- application are very similar to the steps a :term:`Pylons`
- developer must take to do the same. See the `Pylons
- internationalization documentation
- <http://wiki.pylonshq.com/display/pylonsdocs/Internationalization+and+Localization>`_
- for more information.
+ The steps a developer must take to work with :term:`gettext` :term:`message
+ catalog` files within a :app:`Pyramid` application are very similar to the
+ steps a :term:`Pylons` developer must take to do the same. See the
+ :ref:`Pylons Internationalization and Localization documentation
+ <pylonswebframework:i18n>` for more information.
-GNU gettext uses three types of files in the translation framework,
-``.pot`` files, ``.po`` files and ``.mo`` files.
+GNU gettext uses three types of files in the translation framework, ``.pot``
+files, ``.po`` files, and ``.mo`` files.
``.pot`` (Portable Object Template) files
- A ``.pot`` file is created by a program which searches through your
- project's source code and which picks out every :term:`message
- identifier` passed to one of the ``_()`` functions
- (eg. :term:`translation string` constructions). The list of all
- message identifiers is placed into a ``.pot`` file, which serves as
- a template for creating ``.po`` files.
+ A ``.pot`` file is created by a program which searches through your project's
+ source code and which picks out every :term:`message identifier` passed to
+ one of the ``_()`` functions (e.g., :term:`translation string`
+ constructions). The list of all message identifiers is placed into a ``.pot``
+ file, which serves as a template for creating ``.po`` files.
``.po`` (Portable Object) files
- The list of messages in a ``.pot`` file are translated by a human to
- a particular language; the result is saved as a ``.po`` file.
+ The list of messages in a ``.pot`` file are translated by a human to a
+ particular language; the result is saved as a ``.po`` file.
``.mo`` (Machine Object) files
- A ``.po`` file is turned into a machine-readable binary file, which
- is the ``.mo`` file. Compiling the translations to machine code
- makes the localized program start faster.
+ A ``.po`` file is turned into a machine-readable binary file, which is the
+ ``.mo`` file. Compiling the translations to machine code makes the
+ localized program start faster.
The tools for working with :term:`gettext` translation files related to a
-:app:`Pyramid` application are :term:`Lingua` and :term:`Gettext`. Lingua
-can scrape i18n references out of Python and Chameleon files and create
-the ``.pot`` file. Gettext includes ``msgmerge`` tool to update a ``.po`` file
-from an updated ``.pot`` file and ``msgfmt`` to compile ``.po`` files to
-``.mo`` files.
+:app:`Pyramid` application are :term:`Lingua` and :term:`Gettext`. Lingua can
+scrape i18n references out of Python and Chameleon files and create the
+``.pot`` file. Gettext includes ``msgmerge`` tool to update a ``.po`` file from
+an updated ``.pot`` file and ``msgfmt`` to compile ``.po`` files to ``.mo``
+files.
.. index::
single: Gettext
@@ -263,19 +252,18 @@ from an updated ``.pot`` file and ``msgfmt`` to compile ``.po`` files to
Installing Lingua and Gettext
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In order for the commands related to working with ``gettext`` translation
-files to work properly, you will need to have :term:`Lingua` and
-:term:`Gettext` installed into the same environment in which :app:`Pyramid` is
-installed.
+In order for the commands related to working with ``gettext`` translation files
+to work properly, you will need to have :term:`Lingua` and :term:`Gettext`
+installed into the same environment in which :app:`Pyramid` is installed.
Installation on UNIX
++++++++++++++++++++
Gettext is often already installed on UNIX systems. You can check if it is
installed by testing if the ``msgfmt`` command is available. If it is not
-available you can install it through the packaging system from your OS;
-the package name is almost always ``gettext``. For example on a Debian or
-Ubuntu system run this command:
+available you can install it through the packaging system from your OS; the
+package name is almost always ``gettext``. For example on a Debian or Ubuntu
+system run this command:
.. code-block:: text
@@ -283,8 +271,7 @@ Ubuntu system run this command:
Installing Lingua is done with the Python packaging tools. If the
:term:`virtualenv` into which you've installed your :app:`Pyramid` application
-lives in ``/my/virtualenv``, you can install Lingua
-like so:
+lives in ``/my/virtualenv``, you can install Lingua like so:
.. code-block:: text
@@ -296,11 +283,10 @@ Installation on Windows
There are several ways to install Gettext on Windows: it is included in the
`Cygwin <http://www.cygwin.com/>`_ collection, or you can use the `installer
-from the GnuWin32 <http://gnuwin32.sourceforge.net/packages/gettext.htm>`_
-or compile it yourself. Make sure the installation path is added to your
+from the GnuWin32 <http://gnuwin32.sourceforge.net/packages/gettext.htm>`_, or
+compile it yourself. Make sure the installation path is added to your
``$PATH``.
-
Installing Lingua is done with the Python packaging tools. If the
:term:`virtualenv` into which you've installed your :app:`Pyramid` application
lives in ``C:\my\virtualenv``, you can install Lingua like so:
@@ -318,7 +304,7 @@ lives in ``C:\my\virtualenv``, you can install Lingua like so:
Extracting Messages from Code and Templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Once Lingua is installed you may extract a message catalog template from the
+Once Lingua is installed, you may extract a message catalog template from the
code and :term:`Chameleon` templates which reside in your :app:`Pyramid`
application. You run a ``pot-create`` command to extract the messages:
@@ -328,8 +314,7 @@ application. You run a ``pot-create`` command to extract the messages:
$ mkdir -p myapplication/locale
$ $VENV/bin/pot-create -o myapplication/locale/myapplication.pot src
-The message catalog ``.pot`` template will end up in:
-
+The message catalog ``.pot`` template will end up in
``myapplication/locale/myapplication.pot``.
@@ -340,12 +325,11 @@ Initializing a Message Catalog File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you've extracted messages into a ``.pot`` file (see
-:ref:`extracting_messages`), to begin localizing the messages present
-in the ``.pot`` file, you need to generate at least one ``.po`` file.
-A ``.po`` file represents translations of a particular set of messages
-to a particular locale. Initialize a ``.po`` file for a specific
-locale from a pre-generated ``.pot`` template by using the ``msginit``
-command from Gettext:
+:ref:`extracting_messages`), to begin localizing the messages present in the
+``.pot`` file, you need to generate at least one ``.po`` file. A ``.po`` file
+represents translations of a particular set of messages to a particular locale.
+Initialize a ``.po`` file for a specific locale from a pre-generated ``.pot``
+template by using the ``msginit`` command from Gettext:
.. code-block:: text
@@ -354,18 +338,15 @@ command from Gettext:
$ mkdir -p es/LC_MESSAGES
$ msginit -l es -o es/LC_MESSAGES/myapplication.po
-This will create a new message catalog ``.po`` file in:
-
+This will create a new message catalog ``.po`` file in
``myapplication/locale/es/LC_MESSAGES/myapplication.po``.
-Once the file is there, it can be worked on by a human translator.
-One tool which may help with this is `Poedit
-<http://www.poedit.net/>`_.
+Once the file is there, it can be worked on by a human translator. One tool
+which may help with this is `Poedit <http://www.poedit.net/>`_.
-Note that :app:`Pyramid` itself ignores the existence of all
-``.po`` files. For a running application to have translations
-available, a ``.mo`` file must exist. See
-:ref:`compiling_message_catalog`.
+Note that :app:`Pyramid` itself ignores the existence of all ``.po`` files.
+For a running application to have translations available, a ``.mo`` file must
+exist. See :ref:`compiling_message_catalog`.
.. index::
pair: updating; message catalog
@@ -373,13 +354,13 @@ available, a ``.mo`` file must exist. See
Updating a Catalog File
~~~~~~~~~~~~~~~~~~~~~~~
-If more translation strings are added to your application, or
-translation strings change, you will need to update existing ``.po``
-files based on changes to the ``.pot`` file, so that the new and
-changed messages can also be translated or re-translated.
+If more translation strings are added to your application, or translation
+strings change, you will need to update existing ``.po`` files based on changes
+to the ``.pot`` file, so that the new and changed messages can also be
+translated or re-translated.
-First, regenerate the ``.pot`` file as per :ref:`extracting_messages`.
-Then use the ``msgmerge`` command from Gettext.
+First, regenerate the ``.pot`` file as per :ref:`extracting_messages`. Then use
+the ``msgmerge`` command from Gettext.
.. code-block:: text
@@ -395,19 +376,21 @@ Then use the ``msgmerge`` command from Gettext.
Compiling a Message Catalog File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Finally, to prepare an application for performing actual runtime
-translations, compile ``.po`` files to ``.mo`` files use the ``msgfmt``
-command from Gettext:
+Finally, to prepare an application for performing actual runtime translations,
+compile ``.po`` files to ``.mo`` files using the ``msgfmt`` command from
+Gettext:
.. code-block:: text
$ cd /place/where/myapplication/setup.py/lives
- $ msgfmt -o myapplication/locale/es/LC_MESSAGES/myapplication.mo myapplication/locale/es/LC_MESSAGES/myapplication.po
+ $ msgfmt -o myapplication/locale/es/LC_MESSAGES/myapplication.mo \
+ myapplication/locale/es/LC_MESSAGES/myapplication.po
-This will create a ``.mo`` file for each ``.po`` file in your
-application. As long as the :term:`translation directory` in which
-the ``.mo`` file ends up in is configured into your application (see :ref:`adding_a_translation_directory`), these
-translations will be available to :app:`Pyramid`.
+This will create a ``.mo`` file for each ``.po`` file in your application. As
+long as the :term:`translation directory` in which the ``.mo`` file ends up in
+is configured into your application (see
+:ref:`adding_a_translation_directory`), these translations will be available to
+:app:`Pyramid`.
.. index::
single: localizer
@@ -418,10 +401,10 @@ 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
-: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
+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
@@ -432,7 +415,7 @@ localizer object if no explicit locale negotiator is registered.
.. note::
- If you need to create a localizer for a locale use the
+ If you need to create a localizer for a locale, use the
:func:`pyramid.i18n.make_localizer` function.
.. index::
@@ -444,9 +427,9 @@ Performing a Translation
~~~~~~~~~~~~~~~~~~~~~~~~
A :term:`localizer` has a ``translate`` method which accepts either a
-:term:`translation string` or a Unicode string and which returns a
-Unicode object representing the translation. So, generating a
-translation in a view component of an application might look like so:
+:term:`translation string` or a Unicode string and which returns a Unicode
+object representing the translation. Generating a translation in a view
+component of an application might look like so:
.. code-block:: python
:linenos:
@@ -469,9 +452,8 @@ locale of the localizer.
.. note::
- If you're using :term:`Chameleon` templates, you don't need
- to pre-translate translation strings this way. See
- :ref:`chameleon_translation_strings`.
+ If you're using :term:`Chameleon` templates, you don't need to pre-translate
+ translation strings this way. See :ref:`chameleon_translation_strings`.
.. index::
single: pluralizing (i18n)
@@ -481,8 +463,7 @@ locale of the localizer.
Performing a Pluralization
~~~~~~~~~~~~~~~~~~~~~~~~~~
-A :term:`localizer` has a ``pluralize`` method with the following
-signature:
+A :term:`localizer` has a ``pluralize`` method with the following signature:
.. code-block:: python
:linenos:
@@ -491,7 +472,7 @@ signature:
...
The simplest case is the ``singular`` and ``plural`` arguments being passed as
-unicode literals. This returns the appropriate literal according to the locale
+Unicode literals. This returns the appropriate literal according to the locale
pluralization rules for the number ``n``, and interpolates ``mapping``.
.. code-block:: python
@@ -502,15 +483,14 @@ pluralization rules for the number ``n``, and interpolates ``mapping``.
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.
+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
+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.
@@ -561,18 +541,17 @@ You can obtain the locale name related to a request by using the
def aview(request):
locale_name = request.locale_name
-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`.
+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.request.Request.locale_name` is first run, the locale
-name is stored on the request object. Subsequent calls to
-: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:
+Once :func:`~pyramid.request.Request.locale_name` is first run, the locale name
+is stored on the request object. Subsequent calls to
+: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:
.. code-block:: python
:linenos:
@@ -592,8 +571,8 @@ You can also obtain the locale name related to a request using the
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 asking for the
+Obtaining the locale name as an attribute of a localizer is equivalent to
+obtaining a locale name by asking for the
:func:`~pyramid.request.Request.locale_name` attribute.
.. index::
@@ -603,20 +582,18 @@ to obtaining a locale name by asking for the
Performing Date Formatting and Currency Formatting
--------------------------------------------------
-:app:`Pyramid` does not itself perform date and currency formatting
-for different locales. However, :term:`Babel` can help you do this
-via the :class:`babel.core.Locale` class. The `Babel documentation
-for this class
+:app:`Pyramid` does not itself perform date and currency formatting for
+different locales. However, :term:`Babel` can help you do this via the
+:class:`babel.core.Locale` class. The `Babel documentation for this class
<http://babel.edgewall.org/wiki/ApiDocs/babel.core#babel.core:Locale>`_
-provides minimal information about how to perform date and currency
-related locale operations. See :ref:`installing_babel` for
-information about how to install Babel.
+provides minimal information about how to perform date and currency related
+locale operations. See :ref:`installing_babel` for information about how to
+install Babel.
-The :class:`babel.core.Locale` class requires a :term:`locale name` as
-an argument to its constructor. You can use :app:`Pyramid` APIs to
-obtain the locale name for a request to pass to the
-:class:`babel.core.Locale` constructor; see
-:ref:`obtaining_the_locale_name`. For example:
+The :class:`babel.core.Locale` class requires a :term:`locale name` as an
+argument to its constructor. You can use :app:`Pyramid` APIs to obtain the
+locale name for a request to pass to the :class:`babel.core.Locale`
+constructor. See :ref:`obtaining_the_locale_name`. For example:
.. code-block:: python
:linenos:
@@ -635,15 +612,14 @@ obtain the locale name for a request to pass to the
Chameleon Template Support for Translation Strings
--------------------------------------------------
-When a :term:`translation string` is used as the subject of textual
-rendering by a :term:`Chameleon` template renderer, it will
-automatically be translated to the requesting user's language if a
-suitable translation exists. This is true of both the ZPT and text
-variants of the Chameleon template renderers.
+When a :term:`translation string` is used as the subject of textual rendering
+by a :term:`Chameleon` template renderer, it will automatically be translated
+to the requesting user's language if a suitable translation exists. This is
+true of both the ZPT and text variants of the Chameleon template renderers.
-For example, in a Chameleon ZPT template, the translation string
-represented by "some_translation_string" in each example below will go
-through translation before being rendered:
+For example, in a Chameleon ZPT template, the translation string represented by
+"some_translation_string" in each example below will go through translation
+before being rendered:
.. code-block:: xml
:linenos:
@@ -668,32 +644,31 @@ through translation before being rendered:
.. XXX the last example above appears to not yet work as of Chameleon
.. 1.2.3
-The features represented by attributes of the ``i18n`` namespace of
-Chameleon will also consult the :app:`Pyramid` translations.
-See http://chameleon.readthedocs.org/en/latest/reference.html#id50.
+The features represented by attributes of the ``i18n`` namespace of Chameleon
+will also consult the :app:`Pyramid` translations. See
+http://chameleon.readthedocs.org/en/latest/reference.html#id50.
.. note::
- Unlike when Chameleon is used outside of :app:`Pyramid`, when it
- is used *within* :app:`Pyramid`, it does not support use of the
- ``zope.i18n`` translation framework. Applications which use
- :app:`Pyramid` should use the features documented in this
- chapter rather than ``zope.i18n``.
+ Unlike when Chameleon is used outside of :app:`Pyramid`, when it is used
+ *within* :app:`Pyramid`, it does not support use of the ``zope.i18n``
+ translation framework. Applications which use :app:`Pyramid` should use the
+ features documented in this chapter rather than ``zope.i18n``.
-Third party :app:`Pyramid` template renderers might not provide
-this support out of the box and may need special code to do an
-equivalent. For those, you can always use the more manual translation
-facility described in :ref:`performing_a_translation`.
+Third party :app:`Pyramid` template renderers might not provide this support
+out of the box and may need special code to do an equivalent. For those, you
+can always use the more manual translation facility described in
+:ref:`performing_a_translation`.
.. index::
single: Mako i18n
-Mako Pyramid I18N Support
+Mako Pyramid i18n Support
-------------------------
-There exists a recipe within the :term:`Pyramid Cookbook` named "Mako
-Internationalization" which explains how to add idiomatic I18N support to
-:term:`Mako` templates.
+There exists a recipe within the :term:`Pyramid Cookbook` named ":ref:`Mako
+Internationalization <cookbook:mako_i18n>`" which explains how to add idiomatic
+i18n support to :term:`Mako` templates.
.. index::
single: localization deployment settings
@@ -705,10 +680,9 @@ Localization-Related Deployment Settings
----------------------------------------
A :app:`Pyramid` application will have a ``pyramid.default_locale_name``
-setting. This value represents the :term:`default locale name` used
-when the :term:`locale negotiator` returns ``None``. Pass it to the
-:mod:`~pyramid.config.Configurator` constructor at startup
-time:
+setting. This value represents the :term:`default locale name` used when the
+:term:`locale negotiator` returns ``None``. Pass it to the
+:mod:`~pyramid.config.Configurator` constructor at startup time:
.. code-block:: python
:linenos:
@@ -729,11 +703,11 @@ application's ``.ini`` file:
pyramid.debug_notfound = false
pyramid.default_locale_name = de
-If this value is not supplied via the Configurator constructor or via a
-config file, it will default to ``en``.
+If this value is not supplied via the Configurator constructor or via a config
+file, it will default to ``en``.
-If this setting is supplied within the :app:`Pyramid` application
-``.ini`` file, it will be available as a settings key:
+If this setting is supplied within the :app:`Pyramid` application ``.ini``
+file, it will be available as a settings key:
.. code-block:: python
:linenos:
@@ -748,34 +722,32 @@ If this setting is supplied within the :app:`Pyramid` application
"Detecting" Available Languages
-------------------------------
-Other systems provide an API that returns the set of "available
-languages" as indicated by the union of all languages in all
-translation directories on disk at the time of the call to the API.
+Other systems provide an API that returns the set of "available languages" as
+indicated by the union of all languages in all translation directories on disk
+at the time of the call to the API.
-It is by design that :app:`Pyramid` doesn't supply such an API.
-Instead, the application itself is responsible for knowing the "available
-languages". The rationale is this: any particular application
-deployment must always know which languages it should be translatable
-to anyway, regardless of which translation files are on disk.
+It is by design that :app:`Pyramid` doesn't supply such an API. Instead the
+application itself is responsible for knowing the "available languages". The
+rationale is this: any particular application deployment must always know which
+languages it should be translatable to anyway, regardless of which translation
+files are on disk.
-Here's why: it's not a given that because translations exist in a
-particular language within the registered set of translation
-directories that this particular deployment wants to allow translation
-to that language. For example, some translations may exist but they
-may be incomplete or incorrect. Or there may be translations to a
-language but not for all translation domains.
+Here's why: it's not a given that because translations exist in a particular
+language within the registered set of translation directories that this
+particular deployment wants to allow translation to that language. For
+example, some translations may exist but they may be incomplete or incorrect.
+Or there may be translations to a language but not for all translation domains.
Any nontrivial application deployment will always need to be able to
-selectively choose to allow only some languages even if that set of
-languages is smaller than all those detected within registered
-translation directories. The easiest way to allow for this is to make
-the application entirely responsible for knowing which languages are
-allowed to be translated to instead of relying on the framework to
-divine this information from translation directory file info.
+selectively choose to allow only some languages even if that set of languages
+is smaller than all those detected within registered translation directories.
+The easiest way to allow for this is to make the application entirely
+responsible for knowing which languages are allowed to be translated to instead
+of relying on the framework to divine this information from translation
+directory file info.
-You can set up a system to allow a deployer to select available
-languages based on convention by using the :mod:`pyramid.settings`
-mechanism:
+You can set up a system to allow a deployer to select available languages based
+on convention by using the :mod:`pyramid.settings` mechanism.
Allow a deployer to modify your application's ``.ini`` file:
@@ -798,8 +770,8 @@ Then as a part of the code of a custom :term:`locale negotiator`:
languages = aslist(request.registry.settings['available_languages'])
# ...
-This is only a suggestion. You can create your own "available
-languages" configuration scheme as necessary.
+This is only a suggestion. You can create your own "available languages"
+configuration scheme as necessary.
.. index::
pair: translation; activating
@@ -814,8 +786,8 @@ languages" configuration scheme as necessary.
Activating Translation
----------------------
-By default, a :app:`Pyramid` application performs no translation.
-To turn translation on, you must:
+By default, a :app:`Pyramid` application performs no translation. To turn
+translation on you must:
- add at least one :term:`translation directory` to your application.
@@ -829,24 +801,23 @@ To turn translation on, you must:
Adding a Translation Directory
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-:term:`gettext` is the underlying machinery behind the
-:app:`Pyramid` translation machinery. A translation directory is a
-directory organized to be useful to :term:`gettext`. A translation
-directory usually includes a listing of language directories, each of
-which itself includes an ``LC_MESSAGES`` directory. Each
-``LC_MESSAGES`` directory should contain one or more ``.mo`` files.
-Each ``.mo`` file represents a :term:`message catalog`, which is used
-to provide translations to your application.
+:term:`gettext` is the underlying machinery behind the :app:`Pyramid`
+translation machinery. A translation directory is a directory organized to be
+useful to :term:`gettext`. A translation directory usually includes a listing
+of language directories, each of which itself includes an ``LC_MESSAGES``
+directory. Each ``LC_MESSAGES`` directory should contain one or more ``.mo``
+files. Each ``.mo`` file represents a :term:`message catalog`, which is used to
+provide translations to your application.
Adding a :term:`translation directory` registers all of its constituent
-:term:`message catalog` files within your :app:`Pyramid` application to
-be available to use for translation services. This includes all of the
-``.mo`` files found within all ``LC_MESSAGES`` directories within each
-locale directory in the translation directory.
+:term:`message catalog` files within your :app:`Pyramid` application to be
+available to use for translation services. This includes all of the ``.mo``
+files found within all ``LC_MESSAGES`` directories within each locale directory
+in the translation directory.
You can add a translation directory imperatively by using the
-:meth:`pyramid.config.Configurator.add_translation_dirs` during
-application startup. For example:
+:meth:`pyramid.config.Configurator.add_translation_dirs` during application
+startup. For example:
.. code-block:: python
:linenos:
@@ -856,10 +827,10 @@ application startup. For example:
'another.application:locale/')
A message catalog in a translation directory added via
-:meth:`~pyramid.config.Configurator.add_translation_dirs`
-will be merged into translations from a message catalog added earlier
-if both translation directories contain translations for the same
-locale and :term:`translation domain`.
+:meth:`~pyramid.config.Configurator.add_translation_dirs` will be merged into
+translations from a message catalog added earlier if both translation
+directories contain translations for the same locale and :term:`translation
+domain`.
.. index::
pair: setting; locale
@@ -867,32 +838,29 @@ locale and :term:`translation domain`.
Setting the Locale
~~~~~~~~~~~~~~~~~~
-When the *default locale negotiator* (see
-:ref:`default_locale_negotiator`) is in use, you can inform
-:app:`Pyramid` of the current locale name by doing any of these
-things before any translations need to be performed:
+When the *default locale negotiator* (see :ref:`default_locale_negotiator`) is
+in use, you can inform :app:`Pyramid` of the current locale name by doing any
+of these things before any translations need to be performed:
-- Set the ``_LOCALE_`` attribute of the request to a valid locale name
- (usually directly within view code). E.g. ``request._LOCALE_ =
- 'de'``.
+- Set the ``_LOCALE_`` attribute of the request to a valid locale name (usually
+ directly within view code), e.g., ``request._LOCALE_ = 'de'``.
-- Ensure that a valid locale name value is in the ``request.params``
- dictionary under the key named ``_LOCALE_``. This is usually the
- result of passing a ``_LOCALE_`` value in the query string or in the
- body of a form post associated with a request. For example,
- visiting ``http://my.application?_LOCALE_=de``.
+- Ensure that a valid locale name value is in the ``request.params`` dictionary
+ under the key named ``_LOCALE_``. This is usually the result of passing a
+ ``_LOCALE_`` value in the query string or in the body of a form post
+ associated with a request. For example, visiting
+ ``http://my.application?_LOCALE_=de``.
- Ensure that a valid locale name value is in the ``request.cookies``
- dictionary under the key named ``_LOCALE_``. This is usually the
- result of setting a ``_LOCALE_`` cookie in a prior response,
- e.g. ``response.set_cookie('_LOCALE_', 'de')``.
+ dictionary under the key named ``_LOCALE_``. This is usually the result of
+ setting a ``_LOCALE_`` cookie in a prior response, e.g.,
+ ``response.set_cookie('_LOCALE_', 'de')``.
.. note::
If this locale negotiation scheme is inappropriate for a particular
- application, you can configure a custom :term:`locale negotiator`
- function into that application as required. See
- :ref:`custom_locale_negotiator`.
+ application, you can configure a custom :term:`locale negotiator` function
+ into that application as required. See :ref:`custom_locale_negotiator`.
.. index::
single: locale negotiator
@@ -902,57 +870,55 @@ things before any translations need to be performed:
Locale Negotiators
------------------
-A :term:`locale negotiator` informs the operation of a
-:term:`localizer` by telling it what :term:`locale name` is related to
-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.request.Request.locale_name` is accessed or
-when :func:`~pyramid.i18n.negotiate_locale_name` is invoked.
+A :term:`locale negotiator` informs the operation of a :term:`localizer` by
+telling it what :term:`locale name` is related to 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.request.Request.locale_name` is accessed or when
+:func:`~pyramid.i18n.negotiate_locale_name` is invoked.
.. _default_locale_negotiator:
The Default Locale Negotiator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Most applications can make use of the default locale negotiator, which
-requires no additional coding or configuration.
+Most applications can make use of the default locale negotiator, which 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 determine the locale name.
+:class:`~pyramid.i18n.default_locale_negotiator` uses the following 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
- for an :term:`event`).
+- First the negotiator looks for the ``_LOCALE_`` attribute of the request
+ object (possibly set directly by view code or by a listener for an
+ :term:`event`).
- Then it looks for the ``request.params['_LOCALE_']`` value.
- Then it looks for the ``request.cookies['_LOCALE_']`` value.
-- If no locale can be found via the request, it falls back to using
- the :term:`default locale name` (see
- :ref:`localization_deployment_settings`).
+- If no locale can be found via the request, it falls back to using the
+ :term:`default locale name` (see :ref:`localization_deployment_settings`).
-- Finally, if the default locale name is not explicitly set, it uses
- the locale name ``en``.
+- Finally if the default locale name is not explicitly set, it uses the locale
+ name ``en``.
.. _custom_locale_negotiator:
Using a Custom Locale Negotiator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Locale negotiation is sometimes policy-laden and complex. If the
-(simple) default locale negotiation scheme described in
-:ref:`activating_translation` is inappropriate for your application,
-you may create and a special :term:`locale negotiator`. Subsequently
-you may override the default locale negotiator by adding your newly
-created locale negotiator to your application's configuration.
+Locale negotiation is sometimes policy-laden and complex. If the (simple)
+default locale negotiation scheme described in :ref:`activating_translation` is
+inappropriate for your application, you may create a special :term:`locale
+negotiator`. Subsequently you may override the default locale negotiator by
+adding your newly created locale negotiator to your application's
+configuration.
-A locale negotiator is simply a callable which
-accepts a request and returns a single :term:`locale name` or ``None``
-if no locale can be determined.
+A locale negotiator is simply a callable which accepts a request and returns a
+single :term:`locale name` or ``None`` if no locale can be determined.
Here's an implementation of a simple locale negotiator:
@@ -963,16 +929,14 @@ Here's an implementation of a simple locale negotiator:
locale_name = request.params.get('my_locale')
return locale_name
-If a locale negotiator returns ``None``, it signifies to
-:app:`Pyramid` that the default application locale name should be
-used.
+If a locale negotiator returns ``None``, it signifies to :app:`Pyramid` that
+the default application locale name should be used.
You may add your newly created locale negotiator to your application's
configuration by passing an object which can act as the negotiator (or a
:term:`dotted Python name` referring to the object) as the
-``locale_negotiator`` argument of the
-:class:`~pyramid.config.Configurator` instance during application
-startup. For example:
+``locale_negotiator`` argument of the :class:`~pyramid.config.Configurator`
+instance during application startup. For example:
.. code-block:: python
:linenos:
@@ -980,9 +944,8 @@ startup. For example:
from pyramid.config import Configurator
config = Configurator(locale_negotiator=my_locale_negotiator)
-Alternately, use the
-:meth:`pyramid.config.Configurator.set_locale_negotiator`
-method.
+Alternatively, use the
+:meth:`pyramid.config.Configurator.set_locale_negotiator` method.
For example:
@@ -992,4 +955,3 @@ For example:
from pyramid.config import Configurator
config = Configurator()
config.set_locale_negotiator(my_locale_negotiator)
-
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index a825b61b9..26d458727 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -1,7 +1,7 @@
.. _installing_chapter:
Installing :app:`Pyramid`
-============================
+=========================
.. index::
single: install preparation
@@ -15,8 +15,8 @@ You will need `Python <http://python.org>`_ version 2.6 or better to run
.. sidebar:: Python Versions
As of this writing, :app:`Pyramid` has been tested under Python 2.6, Python
- 2.7, Python 3.2, Python 3.3, Python 3.4 and PyPy 2.2. :app:`Pyramid` does
- not run under any version of Python before 2.6.
+ 2.7, Python 3.2, Python 3.3, Python 3.4, PyPy, and PyPy3. :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
@@ -32,12 +32,12 @@ dependency will fall back to using pure Python instead.
For Mac OS X Users
~~~~~~~~~~~~~~~~~~
-Python comes pre-installed on Mac OS X, but due to Apple's release cycle,
-it is often out of date. 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.
+Python comes pre-installed on Mac OS X, but due to Apple's release cycle, it is
+often out of date. 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.
You can install the latest verion of Python for Mac OS X from the binaries on
-`python.org <https://www.python.org/download/mac/>`_.
+`python.org <https://www.python.org/downloads/mac-osx/>`_.
Alternatively, you can use the `homebrew <http://brew.sh/>`_ package manager.
@@ -52,7 +52,7 @@ Alternatively, you can use the `homebrew <http://brew.sh/>`_ package manager.
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 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
@@ -90,12 +90,12 @@ 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.
+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.
+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:
@@ -128,7 +128,7 @@ Once these steps are performed, the Python interpreter will be invokable via
.. index::
pair: install; Python (from package, Windows)
-If You Don't Yet Have A Python Interpreter (Windows)
+If You Don't Yet Have a Python Interpreter (Windows)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If your Windows system doesn't have a Python interpreter, you'll need to
@@ -153,7 +153,7 @@ also need to download and install the Python for Windows extensions.
.. _installing_unix:
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
@@ -204,7 +204,7 @@ it using the Python interpreter into which you want to install setuptools.
$ python ez_setup.py
-Once this command is invoked, setuptools 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:
@@ -285,8 +285,8 @@ it's an absolute path.
acceptable (and desirable) to create a virtualenv as a normal user.
-Installing :app:`Pyramid` Into the Virtual Python Environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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:
@@ -301,9 +301,9 @@ 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.
+ 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
@@ -311,7 +311,7 @@ complete, as it downloads and installs a number of dependencies.
.. _installing_windows:
Installing :app:`Pyramid` on a Windows System
--------------------------------------------------
+---------------------------------------------
You can use Pyramid on Windows under Python 2 or 3.
@@ -382,4 +382,3 @@ WebOb, PasteDeploy, and others are installed.
Additionally, as chronicled in :ref:`project_narr`, scaffolds will be
registered, which make it easy to start a new :app:`Pyramid` project.
-
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index a37d74c9b..7906dd85d 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -7,7 +7,7 @@
single: framework
:app:`Pyramid` Introduction
-==============================
+===========================
:app:`Pyramid` is a general, open source, Python web application development
*framework*. Its primary goal is to make it easier for a Python developer to
@@ -15,40 +15,39 @@ create web applications.
.. sidebar:: Frameworks vs. Libraries
- A *framework* differs from a *library* in one very important way:
- library code is always *called* by code that you write, while a
- framework always *calls* code that you write. Using a set of
- libraries to create an application is usually easier than using a
- framework initially, because you can choose to cede control to
- library code you have not authored very selectively. But when you
- use a framework, you are required to cede a greater portion of
- control to code you have not authored: code that resides in the
- framework itself. You needn't use a framework at all to create a
- web application using Python. A rich set of libraries already
- exists for the platform. In practice, however, using a framework
- to create an application is often more practical than rolling your
- own via a set of libraries if the framework provides a set of
- facilities that fits your application requirements.
+ A *framework* differs from a *library* in one very important way: library
+ code is always *called* by code that you write, while a framework always
+ *calls* code that you write. Using a set of libraries to create an
+ application is usually easier than using a framework initially, because you
+ can choose to cede control to library code you have not authored very
+ selectively. But when you use a framework, you are required to cede a
+ greater portion of control to code you have not authored: code that resides
+ in the framework itself. You needn't use a framework at all to create a web
+ application using Python. A rich set of libraries already exists for the
+ platform. In practice, however, using a framework to create an application
+ is often more practical than rolling your own via a set of libraries if the
+ framework provides a set of facilities that fits your application
+ requirements.
Pyramid attempts to follow these design and engineering principles:
Simplicity
:app:`Pyramid` takes a *"pay only for what you eat"* approach. You can get
- results even if you have only a partial understanding of :app:`Pyramid`.
- It doesn’t force you to use any particular technology to produce an
- application, and we try to keep the core set of concepts that you need to
- understand to a minimum.
+ results even if you have only a partial understanding of :app:`Pyramid`. It
+ doesn't force you to use any particular technology to produce an application,
+ and we try to keep the core set of concepts that you need to understand to a
+ minimum.
Minimalism
- :app:`Pyramid` tries to solve only the fundamental problems of creating
- a web application: the mapping of URLs to code, templating, security and
- serving static assets. We consider these to be the core activities that are
- common to nearly all web applications.
+ :app:`Pyramid` tries to solve only the fundamental problems of creating a web
+ application: the mapping of URLs to code, templating, security, and serving
+ static assets. We consider these to be the core activities that are common to
+ nearly all web applications.
Documentation
- Pyramid's minimalism means that it is easier for us to maintain complete
- and up-to-date documentation. It is our goal that no aspect of Pyramid
- is undocumented.
+ Pyramid's minimalism means that it is easier for us to maintain complete and
+ up-to-date documentation. It is our goal that no aspect of Pyramid is
+ undocumented.
Speed
:app:`Pyramid` is designed to provide noticeably fast execution for common
@@ -56,68 +55,66 @@ Speed
Reliability
:app:`Pyramid` is developed conservatively and tested exhaustively. Where
- Pyramid source code is concerned, our motto is: "If it ain’t tested, it’s
+ Pyramid source code is concerned, our motto is: "If it ain't tested, it's
broke".
Openness
- As with Python, the Pyramid software is distributed under a `permissive
- open source license <http://repoze.org/license.html>`_.
+ As with Python, the Pyramid software is distributed under a `permissive open
+ source license <http://repoze.org/license.html>`_.
.. _what_makes_pyramid_unique:
-What Makes Pyramid Unique
+What makes Pyramid unique
-------------------------
Understandably, people don't usually want to hear about squishy engineering
-principles, they want to hear about concrete stuff that solves their
-problems. With that in mind, what would make someone want to use Pyramid
-instead of one of the many other web frameworks available today? What makes
-Pyramid unique?
-
-This is a hard question to answer, because there are lots of excellent
-choices, and it's actually quite hard to make a wrong choice, particularly in
-the Python web framework market. But one reasonable answer is this: you can
-write very small applications in Pyramid without needing to know a lot.
-"What?", you say, "that can't possibly be a unique feature, lots of other web
-frameworks let you do that!" Well, you're right. But unlike many other
-systems, you can also write very large applications in Pyramid if you learn a
-little more about it. Pyramid will allow you to become productive quickly,
-and will grow with you; it won't hold you back when your application is small
-and it won't get in your way when your application becomes large. "Well
-that's fine," you say, "lots of other frameworks let me write large apps
-too." Absolutely. But other Python web frameworks don't seamlessly let you
-do both. They seem to fall into two non-overlapping categories: frameworks
-for "small apps" and frameworks for "big apps". The "small app" frameworks
-typically sacrifice "big app" features, and vice versa.
+principles; they want to hear about concrete stuff that solves their problems.
+With that in mind, what would make someone want to use Pyramid instead of one
+of the many other web frameworks available today? What makes Pyramid unique?
+
+This is a hard question to answer because there are lots of excellent choices,
+and it's actually quite hard to make a wrong choice, particularly in the Python
+web framework market. But one reasonable answer is this: you can write very
+small applications in Pyramid without needing to know a lot. "What?" you say.
+"That can't possibly be a unique feature. Lots of other web frameworks let you
+do that!" Well, you're right. But unlike many other systems, you can also
+write very large applications in Pyramid if you learn a little more about it.
+Pyramid will allow you to become productive quickly, and will grow with you. It
+won't hold you back when your application is small, and it won't get in your
+way when your application becomes large. "Well that's fine," you say. "Lots of
+other frameworks let me write large apps, too." Absolutely. But other Python
+web frameworks don't seamlessly let you do both. They seem to fall into two
+non-overlapping categories: frameworks for "small apps" and frameworks for "big
+apps". The "small app" frameworks typically sacrifice "big app" features, and
+vice versa.
We don't think it's a universally reasonable suggestion to write "small apps"
in a "small framework" and "big apps" in a "big framework". You can't really
-know to what size every application will eventually grow. We don't really
-want to have to rewrite a previously small application in another framework
-when it gets "too big". We believe the current binary distinction between
-frameworks for small and large applications is just false; a well-designed
-framework should be able to be good at both. Pyramid strives to be that kind
-of framework.
-
-To this end, Pyramid provides a set of features that, combined, are unique
+know to what size every application will eventually grow. We don't really want
+to have to rewrite a previously small application in another framework when it
+gets "too big". We believe the current binary distinction between frameworks
+for small and large applications is just false. A well-designed framework
+should be able to be good at both. Pyramid strives to be that kind of
+framework.
+
+To this end, Pyramid provides a set of features that combined are unique
amongst Python web frameworks. Lots of other frameworks contain some
-combination of these features; Pyramid of course actually stole many of them
-from those other frameworks. But Pyramid is the only one that has all of
-them in one place, documented appropriately, and useful a la carte without
+combination of these features. Pyramid of course actually stole many of them
+from those other frameworks. But Pyramid is the only one that has all of them
+in one place, documented appropriately, and useful *à la carte* without
necessarily paying for the entire banquet. These are detailed below.
Single-file applications
~~~~~~~~~~~~~~~~~~~~~~~~
-You can write a Pyramid application that lives entirely in one Python file,
-not unlike existing Python microframeworks. This is beneficial for one-off
-prototyping, bug reproduction, and very small applications. These
-applications are easy to understand because all the information about the
-application lives in a single place, and you can deploy them without needing
-to understand much about Python distributions and packaging. Pyramid isn't
-really marketed as a microframework, but it allows you to do almost
-everything that frameworks that are marketed as micro offer in very similar
-ways.
+You can write a Pyramid application that lives entirely in one Python file, not
+unlike existing Python microframeworks. This is beneficial for one-off
+prototyping, bug reproduction, and very small applications. These applications
+are easy to understand because all the information about the application lives
+in a single place, and you can deploy them without needing to understand much
+about Python distributions and packaging. Pyramid isn't really marketed as a
+microframework, but it allows you to do almost everything that frameworks that
+are marketed as "micro" offer in very similar ways.
.. literalinclude:: helloworld.py
@@ -142,26 +139,25 @@ decorators to localize the configuration. For example:
def fred_view(request):
return Response('fred')
-However, unlike some other systems, using decorators for Pyramid
-configuration does not make your application difficult to extend, test or
-reuse. The :class:`~pyramid.view.view_config` decorator, for example, does
-not actually *change* the input or output of the function it decorates, so
-testing it is a "WYSIWYG" operation; you don't need to understand the
-framework to test your own code, you just behave as if the decorator is not
-there. You can also instruct Pyramid to ignore some decorators, or use
-completely imperative configuration instead of decorators to add views.
-Pyramid decorators are inert instead of eager: you detect and activate them
-with a :term:`scan`.
+However, unlike some other systems, using decorators for Pyramid configuration
+does not make your application difficult to extend, test, or reuse. The
+:class:`~pyramid.view.view_config` decorator, for example, does not actually
+*change* the input or output of the function it decorates, so testing it is a
+"WYSIWYG" operation. You don't need to understand the framework to test your
+own code. You just behave as if the decorator is not there. You can also
+instruct Pyramid to ignore some decorators, or use completely imperative
+configuration instead of decorators to add views. Pyramid decorators are inert
+instead of eager. You detect and activate them with a :term:`scan`.
Example: :ref:`mapping_views_using_a_decorator_section`.
URL generation
~~~~~~~~~~~~~~
-Pyramid is capable of generating URLs for resources, routes, and static
-assets. Its URL generation APIs are easy to use and flexible. If you use
-Pyramid's various APIs for generating URLs, you can change your configuration
-around arbitrarily without fear of breaking a link on one of your web pages.
+Pyramid is capable of generating URLs for resources, routes, and static assets.
+Its URL generation APIs are easy to use and flexible. If you use Pyramid's
+various APIs for generating URLs, you can change your configuration around
+arbitrarily without fear of breaking a link on one of your web pages.
Example: :ref:`generating_route_urls`.
@@ -169,30 +165,30 @@ Static file serving
~~~~~~~~~~~~~~~~~~~
Pyramid is perfectly willing to serve static files itself. It won't make you
-use some external web server to do that. You can even serve more than one
-set of static files in a single Pyramid web application (e.g. ``/static`` and
-``/static2``). You can also, optionally, place your files on an external web
-server and ask Pyramid to help you generate URLs to those files, so you can
-use Pyramid's internal fileserving while doing development, and a faster
-static file server in production without changing any code.
+use some external web server to do that. You can even serve more than one set
+of static files in a single Pyramid web application (e.g., ``/static`` and
+``/static2``). You can optionally place your files on an external web server
+and ask Pyramid to help you generate URLs to those files. This let's you use
+Pyramid's internal file serving while doing development, and a faster static
+file server in production, without changing any code.
Example: :ref:`static_assets_section`.
-Fully Interactive Development
+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
+pages and automatically restart the application to incorporate changed Python
code. Plain old ``print()`` 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
-allows you access to framework data such as the routes configured, the last
-renderings performed, the current set of packages installed, SQLAlchemy
-queries run, logging data, and various other facts. When an exception
-occurs, you can use its interactive debugger to poke around right in your
-browser to try to determine the cause of the exception. It's handy.
+allows you access to framework data, such as the routes configured, the last
+renderings performed, the current set of packages installed, SQLAlchemy queries
+run, logging data, and various other facts. When an exception occurs, you can
+use its interactive debugger to poke around right in your browser to try to
+determine the cause of the exception. It's handy.
Example: :ref:`debug_toolbar`.
@@ -200,46 +196,46 @@ Debugging settings
~~~~~~~~~~~~~~~~~~
Pyramid has debugging settings that allow you to print Pyramid runtime
-information to the console when things aren't behaving as you're expecting.
-For example, you can turn on "debug_notfound", which prints an informative
-message to the console every time a URL does not match any view. You can
-turn on "debug_authorization", which lets you know why a view execution was
+information to the console when things aren't behaving as you're expecting. For
+example, you can turn on ``debug_notfound``, which prints an informative
+message to the console every time a URL does not match any view. You can turn
+on ``debug_authorization``, which lets you know why a view execution was
allowed or denied by printing a message to the console. These features are
useful for those WTF moments.
There are also a number of commands that you can invoke within a Pyramid
-environment that allow you to introspect the configuration of your system:
-``proutes`` shows all configured routes for an application in the order
-they'll be evaluated for matching; ``pviews`` shows all configured views for
-any given URL. These are also WTF-crushers in some circumstances.
+environment that allow you to introspect the configuration of your system.
+``proutes`` shows all configured routes for an application in the order they'll
+be evaluated for matching. ``pviews`` shows all configured views for any given
+URL. These are also WTF-crushers in some circumstances.
Examples: :ref:`debug_authorization_section` and :ref:`command_line_chapter`.
Add-ons
-~~~~~~~~
+~~~~~~~
Pyramid has an extensive set of add-ons held to the same quality standards as
-the Pyramid core itself. Add-ons are packages which provide functionality
-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
+the Pyramid core itself. Add-ons are packages which provide functionality 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Pyramid has a structured, unified concept of a :term:`view callable`.
-View callables can be functions, methods of classes, or even instances. When
-you add a new view callable, you can choose to make it a function or a method
-of a class; in either case, Pyramid treats it largely the same way. You can
-change your mind later, and move code between methods of classes and
-functions. A collection of similar view callables can be attached to a
-single class as methods, if that floats your boat, and they can share
-initialization code as necessary. All kinds of views are easy to understand
-and use and operate similarly. There is no phony distinction between them;
-they can be used for the same purposes.
+Pyramid has a structured, unified concept of a :term:`view callable`. View
+callables can be functions, methods of classes, or even instances. When you
+add a new view callable, you can choose to make it a function or a method of a
+class. In either case Pyramid treats it largely the same way. You can change
+your mind later and move code between methods of classes and functions. A
+collection of similar view callables can be attached to a single class as
+methods, if that floats your boat, and they can share initialization code as
+necessary. All kinds of views are easy to understand and use, and operate
+similarly. There is no phony distinction between them. They can be used for
+the same purposes.
Here's a view callable defined as a function:
@@ -282,22 +278,22 @@ Here's a few views defined as methods of a class instead:
Asset specifications
~~~~~~~~~~~~~~~~~~~~
-Asset specifications are strings that contain both a Python package name and
-a file or directory name, e.g. ``MyPackage:static/index.html``. Use of these
-specifications is omnipresent in Pyramid. An asset specification can refer
-to a template, a translation directory, or any other package-bound static
-resource. This makes a system built on Pyramid extensible, because you don't
+Asset specifications are strings that contain both a Python package name and a
+file or directory name, e.g., ``MyPackage:static/index.html``. Use of these
+specifications is omnipresent in Pyramid. An asset specification can refer to
+a template, a translation directory, or any other package-bound static
+resource. This makes a system built on Pyramid extensible because you don't
have to rely on globals ("*the* static directory") or lookup schemes ("*the*
ordered set of template directories") to address your files. You can move
files around as necessary, and include other packages that may not share your
system's templates or static files without encountering conflicts.
-Because asset specifications are used heavily in Pyramid, we've also provided
-a way to allow users to override assets. Say you love a system that someone
-else has created with Pyramid but you just need to change "that one template"
-to make it all better. No need to fork the application. Just override the
-asset specification for that template with your own inside a wrapper, and
-you're good to go.
+Because asset specifications are used heavily in Pyramid, we've also provided a
+way to allow users to override assets. Say you love a system that someone else
+has created with Pyramid but you just need to change "that one template" to
+make it all better. No need to fork the application. Just override the asset
+specification for that template with your own inside a wrapper, and you're good
+to go.
Examples: :ref:`asset_specifications` and :ref:`overriding_assets_section`.
@@ -309,12 +305,11 @@ Templating systems such as Mako, Genshi, Chameleon, and Jinja2 can be treated
as renderers. Renderer bindings for all of these templating systems already
exist for use in Pyramid. But if you'd rather use another, it's not a big
deal. Just copy the code from an existing renderer package, and plug in your
-favorite templating system. You'll then be able to use that templating
-system from within Pyramid just as you'd use one of the "built-in" templating
-systems.
+favorite templating system. You'll then be able to use that templating system
+from within Pyramid just as you'd use one of the "built-in" templating systems.
-Pyramid does not make you use a single templating system exclusively. You
-can use multiple templating systems, even in the same project.
+Pyramid does not make you use a single templating system exclusively. You can
+use multiple templating systems, even in the same project.
Example: :ref:`templates_used_directly`.
@@ -322,13 +317,12 @@ Rendered views can return dictionaries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you use a :term:`renderer`, you don't have to return a special kind of
-"webby" ``Response`` object from a view. Instead, you can return a
-dictionary, and Pyramid will take care of converting that dictionary
-to a Response using a template on your behalf. This makes the view easier to
-test, because you don't have to parse HTML in your tests; just make an
-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.
+"webby" ``Response`` object from a view. Instead you can return a dictionary,
+and Pyramid will take care of converting that dictionary to a Response using a
+template on your behalf. This makes the view easier to test, because you don't
+have to parse HTML in your tests. Instead just make an assertion that the view
+returns "the right stuff" in the dictionary. You can write "real" unit tests
+instead of functionally testing all of your views.
.. index::
pair: renderer; explicitly calling
@@ -364,7 +358,7 @@ be rendered to a response on your behalf. The string passed as ``renderer=``
above is an :term:`asset specification`. It is in the form
``packagename:directoryname/filename.ext``. In this case, it refers to the
``mytemplate.pt`` file in the ``templates`` directory within the ``myapp``
-Python package. Asset specifications are omnipresent in Pyramid: see
+Python package. Asset specifications are omnipresent in Pyramid. See
:ref:`intro_asset_specs` for more information.
Example: :ref:`renderers_chapter`.
@@ -373,9 +367,9 @@ Event system
~~~~~~~~~~~~
Pyramid emits *events* during its request processing lifecycle. You can
-subscribe any number of listeners to these events. For example, to be
-notified of a new request, you can subscribe to the ``NewRequest`` event. To
-be notified that a template is about to be rendered, you can subscribe to the
+subscribe any number of listeners to these events. For example, to be notified
+of a new request, you can subscribe to the ``NewRequest`` event. To be
+notified that a template is about to be rendered, you can subscribe to the
``BeforeRender`` event, and so forth. Using an event publishing system as a
framework notification feature instead of hardcoded hook points tends to make
systems based on that framework less brittle.
@@ -383,9 +377,9 @@ systems based on that framework less brittle.
You can also use Pyramid's event system to send your *own* events. For
example, if you'd like to create a system that is itself a framework, and may
want to notify subscribers that a document has just been indexed, you can
-create your own event type (``DocumentIndexed`` perhaps) and send the event
-via Pyramid. Users of this framework can then subscribe to your event like
-they'd subscribe to the events that are normally sent by Pyramid itself.
+create your own event type (``DocumentIndexed`` perhaps) and send the event via
+Pyramid. Users of this framework can then subscribe to your event like they'd
+subscribe to the events that are normally sent by Pyramid itself.
Example: :ref:`events_chapter` and :ref:`event_types`.
@@ -394,8 +388,8 @@ Built-in internationalization
Pyramid ships with internationalization-related features in its core:
localization, pluralization, and creating message catalogs from source files
-and templates. Pyramid allows for a plurality of message catalog via the use
-of translation domains: you can create a system that has its own translations
+and templates. Pyramid allows for a plurality of message catalogs via the use
+of translation domains. You can create a system that has its own translations
without conflict with other translations in other domains.
Example: :ref:`i18n_chapter`.
@@ -403,9 +397,9 @@ Example: :ref:`i18n_chapter`.
HTTP caching
~~~~~~~~~~~~
-Pyramid provides an easy way to associate views with HTTP caching policies.
-You can just tell Pyramid to configure your view with an ``http_cache``
-statement, and it will take care of the rest::
+Pyramid provides an easy way to associate views with HTTP caching policies. You
+can just tell Pyramid to configure your view with an ``http_cache`` statement,
+and it will take care of the rest::
@view_config(http_cache=3600) # 60 minutes
def myview(request): ....
@@ -413,8 +407,8 @@ statement, and it will take care of the rest::
Pyramid will add appropriate ``Cache-Control`` and ``Expires`` headers to
responses generated when this view is invoked.
-See the :meth:`~pyramid.config.Configurator.add_view` method's
-``http_cache`` documentation for more information.
+See the :meth:`~pyramid.config.Configurator.add_view` method's ``http_cache``
+documentation for more information.
Sessions
~~~~~~~~
@@ -423,9 +417,9 @@ Pyramid has built-in HTTP sessioning. This allows you to associate data with
otherwise anonymous users between requests. Lots of systems do this. But
Pyramid also allows you to plug in your own sessioning system by creating some
code that adheres to a documented interface. Currently there is a binding
-package for the third-party Redis sessioning system that does exactly this.
-But if you have a specialized need (perhaps you want to store your session data
-in MongoDB), you can. You can even switch between implementations without
+package for the third-party Redis sessioning system that does exactly this. But
+if you have a specialized need (perhaps you want to store your session data in
+MongoDB), you can. You can even switch between implementations without
changing your application code.
Example: :ref:`sessions_chapter`.
@@ -433,19 +427,18 @@ Example: :ref:`sessions_chapter`.
Speed
~~~~~
-The Pyramid core is, as far as we can tell, at least marginally faster than
-any other existing Python web framework. It has been engineered from the
-ground up for speed. It only does as much work as absolutely necessary when
-you ask it to get a job done. Extraneous function calls and suboptimal
-algorithms in its core codepaths are avoided. It is feasible to get, for
-example, between 3500 and 4000 requests per second from a simple Pyramid view
-on commodity dual-core laptop hardware and an appropriate WSGI server
-(mod_wsgi or gunicorn). In any case, performance statistics are largely
-useless without requirements and goals, but if you need speed, Pyramid will
-almost certainly never be your application's bottleneck; at least no more
-than Python will be a bottleneck.
+The Pyramid core is, as far as we can tell, at least marginally faster than any
+other existing Python web framework. It has been engineered from the ground up
+for speed. It only does as much work as absolutely necessary when you ask it
+to get a job done. Extraneous function calls and suboptimal algorithms in its
+core codepaths are avoided. It is feasible to get, for example, between 3500
+and 4000 requests per second from a simple Pyramid view on commodity dual-core
+laptop hardware and an appropriate WSGI server (mod_wsgi or gunicorn). In any
+case, performance statistics are largely useless without requirements and
+goals, but if you need speed, Pyramid will almost certainly never be your
+application's bottleneck; at least no more than Python will be a bottleneck.
-Example: http://blog.curiasolutions.com/the-great-web-framework-shootout/
+Example: http://blog.curiasolutions.com/pages/the-great-web-framework-shootout.html
Exception views
~~~~~~~~~~~~~~~
@@ -457,9 +450,9 @@ views, but they're only invoked when an exception "bubbles up" to Pyramid
itself. For example, you might register an exception view for the
:exc:`Exception` exception, which will catch *all* exceptions, and present a
pretty "well, this is embarrassing" page. Or you might choose to register an
-exception view for only specific kinds of application-specific exceptions,
-such as an exception that happens when a file is not found, or an exception
-that happens when an action cannot be performed because the user doesn't have
+exception view for only specific kinds of application-specific exceptions, such
+as an exception that happens when a file is not found, or an exception that
+happens when an action cannot be performed because the user doesn't have
permission to do something. In the former case, you can show a pretty "Not
Found" page; in the latter case you might show a login form.
@@ -469,61 +462,59 @@ No singletons
~~~~~~~~~~~~~
Pyramid is written in such a way that it requires your application to have
-exactly zero "singleton" data structures. Or, put another way, Pyramid
-doesn't require you to construct any "mutable globals". Or put even a
-different way, an import of a Pyramid application needn't have any
-"import-time side effects". This is esoteric-sounding, but if you've ever
-tried to cope with parameterizing a Django "settings.py" file for multiple
-installations of the same application, or if you've ever needed to
-monkey-patch some framework fixture so that it behaves properly for your use
-case, or if you've ever wanted to deploy your system using an asynchronous
-server, you'll end up appreciating this feature. It just won't be a problem.
-You can even run multiple copies of a similar but not identically configured
-Pyramid application within the same Python process. This is good for shared
-hosting environments, where RAM is at a premium.
+exactly zero "singleton" data structures. Or put another way, Pyramid doesn't
+require you to construct any "mutable globals". Or put even another different
+way, an import of a Pyramid application needn't have any "import-time side
+effects". This is esoteric-sounding, but if you've ever tried to cope with
+parameterizing a Django ``settings.py`` file for multiple installations of the
+same application, or if you've ever needed to monkey-patch some framework
+fixture so that it behaves properly for your use case, or if you've ever wanted
+to deploy your system using an asynchronous server, you'll end up appreciating
+this feature. It just won't be a problem. You can even run multiple copies of
+a similar but not identically configured Pyramid application within the same
+Python process. This is good for shared hosting environments, where RAM is at
+a premium.
View predicates and many views per route
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unlike many other systems, Pyramid allows you to associate more than one view
-per route. For example, you can create a route with the pattern ``/items``
-and when the route is matched, you can shuffle off the request to one view if
-the request method is GET, another view if the request method is POST, etc.
-A system known as "view predicates" allows for this. Request method matching
-is the most basic thing you can do with a view predicate. You can also
-associate views with other request parameters such as the elements in the
-query string, the Accept header, whether the request is an XHR request or
-not, and lots of other things. This feature allows you to keep your
-individual views "clean"; they won't need much conditional logic, so they'll
-be easier to test.
+per route. For example, you can create a route with the pattern ``/items`` and
+when the route is matched, you can shuffle off the request to one view if the
+request method is GET, another view if the request method is POST, etc. A
+system known as "view predicates" allows for this. Request method matching is
+the most basic thing you can do with a view predicate. You can also associate
+views with other request parameters, such as the elements in the query string,
+the Accept header, whether the request is an XHR request or not, and lots of
+other things. This feature allows you to keep your individual views clean.
+They won't need much conditional logic, so they'll be easier to test.
Example: :ref:`view_configuration_parameters`.
Transaction management
~~~~~~~~~~~~~~~~~~~~~~
-Pyramid's :term:`scaffold` system renders projects that include a
-*transaction management* system, stolen from Zope. When you use this
-transaction management system, you cease being responsible for committing
-your data anymore. Instead, Pyramid takes care of committing: it commits at
-the end of a request or aborts if there's an exception. Why is that a good
-thing? Having a centralized place for transaction management is a great
-thing. If, instead of managing your transactions in a centralized place, you
-sprinkle ``session.commit`` calls in your application logic itself, you can
-wind up in a bad place. Wherever you manually commit data to your database,
-it's likely that some of your other code is going to run *after* your commit.
-If that code goes on to do other important things after that commit, and an
-error happens in the later code, you can easily wind up with inconsistent
-data if you're not extremely careful. Some data will have been written to
-the database that probably should not have. Having a centralized commit
-point saves you from needing to think about this; it's great for lazy people
-who also care about data integrity. Either the request completes
-successfully, and all changes are committed, or it does not, and all changes
-are aborted.
-
-Also, Pyramid's transaction management system allows you to synchronize
-commits between multiple databases, and allows you to do things like
-conditionally send email if a transaction commits, but otherwise keep quiet.
+Pyramid's :term:`scaffold` system renders projects that include a *transaction
+management* system, stolen from Zope. When you use this transaction management
+system, you cease being responsible for committing your data anymore. Instead
+Pyramid takes care of committing: it commits at the end of a request or aborts
+if there's an exception. Why is that a good thing? Having a centralized place
+for transaction management is a great thing. If, instead of managing your
+transactions in a centralized place, you sprinkle ``session.commit`` calls in
+your application logic itself, you can wind up in a bad place. Wherever you
+manually commit data to your database, it's likely that some of your other code
+is going to run *after* your commit. If that code goes on to do other important
+things after that commit, and an error happens in the later code, you can
+easily wind up with inconsistent data if you're not extremely careful. Some
+data will have been written to the database that probably should not have.
+Having a centralized commit point saves you from needing to think about this;
+it's great for lazy people who also care about data integrity. Either the
+request completes successfully, and all changes are committed, or it does not,
+and all changes are aborted.
+
+Pyramid's transaction management system allows you to synchronize commits
+between multiple databases. It also allows you to do things like conditionally
+send email if a transaction commits, but otherwise keep quiet.
Example: :ref:`bfg_sql_wiki_tutorial` (note the lack of commit statements
anywhere in application code).
@@ -531,17 +522,18 @@ anywhere in application code).
Configuration conflict detection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When a system is small, it's reasonably easy to keep it all in your head.
-But when systems grow large, you may have hundreds or thousands of
-configuration statements which add a view, add a route, and so forth.
-Pyramid's configuration system keeps track of your configuration statements,
-and if you accidentally add two that are identical, or Pyramid can't make
-sense out of what it would mean to have both statements active at the same
-time, it will complain loudly at startup time. It's not dumb though: it will
-automatically resolve conflicting configuration statements on its own if you
-use the configuration :meth:`~pyramid.config.Configurator.include` system:
-"more local" statements are preferred over "less local" ones. This allows
-you to intelligently factor large systems into smaller ones.
+When a system is small, it's reasonably easy to keep it all in your head. But
+when systems grow large, you may have hundreds or thousands of configuration
+statements which add a view, add a route, and so forth.
+
+Pyramid's configuration system keeps track of your configuration statements. If
+you accidentally add two that are identical, or Pyramid can't make sense out of
+what it would mean to have both statements active at the same time, it will
+complain loudly at startup time. It's not dumb though. It will automatically
+resolve conflicting configuration statements on its own if you use the
+configuration :meth:`~pyramid.config.Configurator.include` system. "More local"
+statements are preferred over "less local" ones. This allows you to
+intelligently factor large systems into smaller ones.
Example: :ref:`conflict_detection`.
@@ -551,17 +543,17 @@ Configuration extensibility
Unlike other systems, Pyramid provides a structured "include" mechanism (see
:meth:`~pyramid.config.Configurator.include`) that allows you to combine
applications from multiple Python packages. All the configuration statements
-that can be performed in your "main" Pyramid application can also be
-performed by included packages including the addition of views, routes,
-subscribers, and even authentication and authorization policies. You can even
-extend or override an existing application by including another application's
-configuration in your own, overriding or adding new views and routes to
-it. This has the potential to allow you to create a big application out of
-many other smaller ones. For example, if you want to reuse an existing
-application that already has a bunch of routes, you can just use the
-``include`` statement with a ``route_prefix``; the new application will live
-within your application at a URL prefix. It's not a big deal, and requires
-little up-front engineering effort.
+that can be performed in your "main" Pyramid application can also be performed
+by included packages, including the addition of views, routes, subscribers, and
+even authentication and authorization policies. You can even extend or override
+an existing application by including another application's configuration in
+your own, overriding or adding new views and routes to it. This has the
+potential to allow you to create a big application out of many other smaller
+ones. For example, if you want to reuse an existing application that already
+has a bunch of routes, you can just use the ``include`` statement with a
+``route_prefix``. The new application will live within your application at an
+URL prefix. It's not a big deal, and requires little up-front engineering
+effort.
For example:
@@ -584,16 +576,15 @@ For example:
Flexible authentication and authorization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Pyramid includes a flexible, pluggable authentication and authorization
-system. No matter where your user data is stored, or what scheme you'd like
-to use to permit your users to access your data, you can use a predefined
-Pyramid plugpoint to plug in your custom authentication and authorization
-code. If you want to change these schemes later, you can just change it in
-one place rather than everywhere in your code. It also ships with prebuilt
-well-tested authentication and authorization schemes out of the box. But
-what if you don't want to use Pyramid's built-in system? You don't have to.
-You can just write your own bespoke security code as you would in any other
-system.
+Pyramid includes a flexible, pluggable authentication and authorization system.
+No matter where your user data is stored, or what scheme you'd like to use to
+permit your users to access your data, you can use a predefined Pyramid
+plugpoint to plug in your custom authentication and authorization code. If you
+want to change these schemes later, you can just change it in one place rather
+than everywhere in your code. It also ships with prebuilt well-tested
+authentication and authorization schemes out of the box. But what if you don't
+want to use Pyramid's built-in system? You don't have to. You can just write
+your own bespoke security code as you would in any other system.
Example: :ref:`enabling_authorization_policy`.
@@ -601,19 +592,19 @@ Traversal
~~~~~~~~~
:term:`Traversal` is a concept stolen from :term:`Zope`. It allows you to
-create a tree of resources, each of which can be addressed by one or more
-URLs. Each of those resources can have one or more *views* associated with
-it. If your data isn't naturally treelike (or you're unwilling to create a
-treelike representation of your data), you aren't going to find traversal
-very useful. However, traversal is absolutely fantastic for sites that need
-to be arbitrarily extensible: it's a lot easier to add a node to a tree than
-it is to shoehorn a route into an ordered list of other routes, or to create
-another entire instance of an application to service a department and glue
-code to allow disparate apps to share data. It's a great fit for sites that
-naturally lend themselves to changing departmental hierarchies, such as
-content management systems and document management systems. Traversal also
-lends itself well to systems that require very granular security ("Bob can
-edit *this* document" as opposed to "Bob can edit documents").
+create a tree of resources, each of which can be addressed by one or more URLs.
+Each of those resources can have one or more *views* associated with it. If
+your data isn't naturally treelike, or you're unwilling to create a treelike
+representation of your data, you aren't going to find traversal very useful.
+However, traversal is absolutely fantastic for sites that need to be
+arbitrarily extensible. It's a lot easier to add a node to a tree than it is to
+shoehorn a route into an ordered list of other routes, or to create another
+entire instance of an application to service a department and glue code to
+allow disparate apps to share data. It's a great fit for sites that naturally
+lend themselves to changing departmental hierarchies, such as content
+management systems and document management systems. Traversal also lends
+itself well to systems that require very granular security ("Bob can edit
+*this* document" as opposed to "Bob can edit documents").
Examples: :ref:`hello_traversal_chapter` and
:ref:`much_ado_about_traversal_chapter`.
@@ -635,7 +626,7 @@ View response adapters
A lot is made of the aesthetics of what *kinds* of objects you're allowed to
return from view callables in various frameworks. In a previous section in
-this document we showed you that, if you use a :term:`renderer`, you can
+this document, we showed you that, if you use a :term:`renderer`, you can
usually return a dictionary from a view callable instead of a full-on
:term:`Response` object. But some frameworks allow you to return strings or
tuples from view callables. When frameworks allow for this, code looks
@@ -662,12 +653,11 @@ The former is "prettier", right?
Out of the box, if you define the former view callable (the one that simply
returns a string) in Pyramid, when it is executed, Pyramid will raise an
-exception. This is because "explicit is better than implicit", in most
-cases, and by default, Pyramid wants you to return a :term:`Response` object
-from a view callable. This is because there's usually a heck of a lot more
-to a response object than just its body. But if you're the kind of person
-who values such aesthetics, we have an easy way to allow for this sort of
-thing:
+exception. This is because "explicit is better than implicit", in most cases,
+and by default Pyramid wants you to return a :term:`Response` object from a
+view callable. This is because there's usually a heck of a lot more to a
+response object than just its body. But if you're the kind of person who
+values such aesthetics, we have an easy way to allow for this sort of thing:
.. code-block:: python
:linenos:
@@ -733,9 +723,9 @@ Once this is done, both of these view callables will work:
def anotherview(request):
return (403, 'text/plain', "Forbidden")
-Pyramid defaults to explicit behavior, because it's the most generally
-useful, but provides hooks that allow you to adapt the framework to localized
-aesthetic desires.
+Pyramid defaults to explicit behavior, because it's the most generally useful,
+but provides hooks that allow you to adapt the framework to localized aesthetic
+desires.
.. seealso::
@@ -744,9 +734,9 @@ aesthetic desires.
"Global" response object
~~~~~~~~~~~~~~~~~~~~~~~~
-"Constructing these response objects in my view callables is such a chore!
-And I'm way too lazy to register a response adapter, as per the prior
-section," you say. Fine. Be that way:
+"Constructing these response objects in my view callables is such a chore! And
+I'm way too lazy to register a response adapter, as per the prior section," you
+say. Fine. Be that way:
.. code-block:: python
:linenos:
@@ -765,13 +755,13 @@ Automating repetitive configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Does Pyramid's configurator allow you to do something, but you're a little
-adventurous and just want it a little less verbose? Or you'd like to offer
-up some handy configuration feature to other Pyramid users without requiring
-that we change Pyramid? You can extend Pyramid's :term:`Configurator` with
-your own directives. For example, let's say you find yourself calling
+adventurous and just want it a little less verbose? Or you'd like to offer up
+some handy configuration feature to other Pyramid users without requiring that
+we change Pyramid? You can extend Pyramid's :term:`Configurator` with your own
+directives. For example, let's say you find yourself calling
:meth:`pyramid.config.Configurator.add_view` repetitively. Usually you can
-take the boring away by using existing shortcuts, but let's say that this is
-a case where there is no such shortcut:
+take the boring away by using existing shortcuts, but let's say that this is a
+case where there is no such shortcut:
.. code-block:: python
:linenos:
@@ -817,7 +807,7 @@ the Configurator object:
Your previously repetitive configuration lines have now morphed into one line.
-You can share your configuration code with others this way too by packaging
+You can share your configuration code with others this way, too, by packaging
it up and calling :meth:`~pyramid.config.Configurator.add_directive` from
within a function called when another user uses the
:meth:`~pyramid.config.Configurator.include` method against your code.
@@ -826,7 +816,7 @@ within a function called when another user uses the
See also :ref:`add_directive`.
-Programmatic Introspection
+Programmatic introspection
~~~~~~~~~~~~~~~~~~~~~~~~~~
If you're building a large system that other users may plug code into, it's
@@ -836,8 +826,7 @@ at the top of the screen based on an enumeration of views they registered.
This is possible using Pyramid's :term:`introspector`.
-Here's an example of using Pyramid's introspector from within a view
-callable:
+Here's an example of using Pyramid's introspector from within a view callable:
.. code-block:: python
:linenos:
@@ -856,13 +845,13 @@ callable:
See also :ref:`using_introspection`.
-Python 3 Compatibility
+Python 3 compatibility
~~~~~~~~~~~~~~~~~~~~~~
Pyramid and most of its add-ons are Python 3 compatible. If you develop a
Pyramid application today, you won't need to worry that five years from now
-you'll be backwatered because there are language features you'd like to use
-but your framework doesn't support newer Python versions.
+you'll be backwatered because there are language features you'd like to use but
+your framework doesn't support newer Python versions.
Testing
~~~~~~~
@@ -871,11 +860,11 @@ Every release of Pyramid has 100% statement coverage via unit and integration
tests, as measured by the ``coverage`` tool available on PyPI. It also has
greater than 95% decision/condition coverage as measured by the
``instrumental`` tool available on PyPI. It is automatically tested by the
-Jenkins tool on Python 2.6, Python 2.7, Python 3.2 and PyPy after each commit
-to its GitHub repository. Official Pyramid add-ons are held to a similar
-testing standard. We still find bugs in Pyramid and its official add-ons,
-but we've noticed we find a lot more of them while working on other projects
-that don't have a good testing regime.
+Jenkins tool on Python 2.6, Python 2.7, Python 3.2, Python 3.3, Python 3.4,
+PyPy, and PyPy3 after each commit to its GitHub repository. Official Pyramid
+add-ons are held to a similar testing standard. We still find bugs in Pyramid
+and its official add-ons, but we've noticed we find a lot more of them while
+working on other projects that don't have a good testing regime.
Example: http://jenkins.pylonsproject.org/
@@ -883,29 +872,28 @@ Support
~~~~~~~
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
+question on IRC, on the Pylons-discuss mailing list, 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 official support channels. We try to keep it well-lit
-and new-user-friendly.
+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
irc.freenode.net in an IRC client) or the pylons-discuss maillist at
-http://groups.google.com/group/pylons-discuss/ .
+http://groups.google.com/group/pylons-discuss/.
Documentation
~~~~~~~~~~~~~
-It's a constant struggle, but we try to maintain a balance between
-completeness and new-user-friendliness in the official narrative Pyramid
-documentation (concrete suggestions for improvement are always appreciated,
-by the way). We also maintain a "cookbook" of recipes, which are usually
-demonstrations of common integration scenarios, too specific to add to the
-official narrative docs. In any case, the Pyramid documentation is
-comprehensive.
+It's a constant struggle, but we try to maintain a balance between completeness
+and new-user-friendliness in the official narrative Pyramid documentation
+(concrete suggestions for improvement are always appreciated, by the way). We
+also maintain a "cookbook" of recipes, which are usually demonstrations of
+common integration scenarios too specific to add to the official narrative
+docs. In any case, the Pyramid documentation is comprehensive.
-Example: The rest of this documentation and the cookbook at
-http://docs.pylonsproject.org/projects/pyramid_cookbook/dev/ .
+Example: The Pyramid Cookbook at
+http://docs.pylonsproject.org/projects/pyramid-cookbook/en/latest/.
.. index::
single: Pylons Project
@@ -929,34 +917,34 @@ includes details about how :app:`Pyramid` relates to the Pylons Project.
------------------------------------------
The first release of Pyramid's predecessor (named :mod:`repoze.bfg`) was made
-in July of 2008. At the end of 2010, we changed the name of
-:mod:`repoze.bfg` to :app:`Pyramid`. It was merged into the Pylons project
-as :app:`Pyramid` in November of that year.
+in July of 2008. At the end of 2010, we changed the name of :mod:`repoze.bfg`
+to :app:`Pyramid`. It was merged into the Pylons project as :app:`Pyramid` in
+November of that year.
-:app:`Pyramid` was inspired by :term:`Zope`, :term:`Pylons` (version
-1.0) and :term:`Django`. As a result, :app:`Pyramid` borrows several
-concepts and features from each, combining them into a unique web
-framework.
+:app:`Pyramid` was inspired by :term:`Zope`, :term:`Pylons` (version 1.0), and
+:term:`Django`. As a result, :app:`Pyramid` borrows several concepts and
+features from each, combining them into a unique web framework.
-Many features of :app:`Pyramid` trace their origins back to :term:`Zope`.
-Like Zope applications, :app:`Pyramid` applications can be easily extended:
-if you obey certain constraints, the application you produce can be reused,
-modified, re-integrated, or extended by third-party developers without
-forking the original application. The concepts of :term:`traversal` and
-declarative security in :app:`Pyramid` were pioneered first in Zope.
+Many features of :app:`Pyramid` trace their origins back to :term:`Zope`. Like
+Zope applications, :app:`Pyramid` applications can be easily extended. If you
+obey certain constraints, the application you produce can be reused, modified,
+re-integrated, or extended by third-party developers without forking the
+original application. The concepts of :term:`traversal` and declarative
+security in :app:`Pyramid` were pioneered first in Zope.
The :app:`Pyramid` concept of :term:`URL dispatch` is inspired by the
-:term:`Routes` system used by :term:`Pylons` version 1.0. Like Pylons
-version 1.0, :app:`Pyramid` is mostly policy-free. It makes no
-assertions about which database you should use, and its built-in
-templating facilities are included only for convenience. In essence,
-it only supplies a mechanism to map URLs to :term:`view` code, along
-with a set of conventions for calling those views. You are free to
-use third-party components that fit your needs in your applications.
-
-The concept of :term:`view` is used by :app:`Pyramid` mostly as it would be
-by Django. :app:`Pyramid` has a documentation culture more like Django's
-than like Zope's.
+:term:`Routes` system used by :term:`Pylons` version 1.0. Like Pylons version
+1.0, :app:`Pyramid` is mostly policy-free. It makes no assertions about which
+database you should use. Pyramid no longer has built-in templating facilities
+as of version 1.5a2, but instead officially supports bindings for templating
+languages, including Chameleon, Jinja2, and Mako. In essence, it only supplies
+a mechanism to map URLs to :term:`view` code, along with a set of conventions
+for calling those views. You are free to use third-party components that fit
+your needs in your applications.
+
+The concept of :term:`view` is used by :app:`Pyramid` mostly as it would be by
+Django. :app:`Pyramid` has a documentation culture more like Django's than
+like Zope's.
Like :term:`Pylons` version 1.0, but unlike :term:`Zope`, a :app:`Pyramid`
application developer may use completely imperative code to perform common
@@ -964,34 +952,34 @@ framework configuration tasks such as adding a view or a route. In Zope,
:term:`ZCML` is typically required for similar purposes. In :term:`Grok`, a
Zope-based web framework, :term:`decorator` objects and class-level
declarations are used for this purpose. Out of the box, Pyramid supports
-imperative and decorator-based configuration; :term:`ZCML` may be used via an
+imperative and decorator-based configuration. :term:`ZCML` may be used via an
add-on package named ``pyramid_zcml``.
-Also unlike :term:`Zope` and unlike other "full-stack" frameworks such
-as :term:`Django`, :app:`Pyramid` makes no assumptions about which
-persistence mechanisms you should use to build an application. Zope
-applications are typically reliant on :term:`ZODB`; :app:`Pyramid`
-allows you to build :term:`ZODB` applications, but it has no reliance
-on the ZODB software. Likewise, :term:`Django` tends to assume that
-you want to store your application's data in a relational database.
-:app:`Pyramid` makes no such assumption; it allows you to use a
-relational database but doesn't encourage or discourage the decision.
-
-Other Python web frameworks advertise themselves as members of a class
-of web frameworks named `model-view-controller
-<http://en.wikipedia.org/wiki/Model–view–controller>`_ frameworks.
-Insofar as this term has been claimed to represent a class of web
-frameworks, :app:`Pyramid` also generally fits into this class.
-
-.. sidebar:: You Say :app:`Pyramid` is MVC, But Where's The Controller?
-
- The :app:`Pyramid` authors believe that the MVC pattern just doesn't
- really fit the web very well. In a :app:`Pyramid` application, there is a
- resource tree, which represents the site structure, and views, which tend
- to present the data stored in the resource tree and a user-defined "domain
- model". However, no facility provided *by the framework* actually
- necessarily maps to the concept of a "controller" or "model". So if you
- had to give it some acronym, I guess you'd say :app:`Pyramid` is actually
- an "RV" framework rather than an "MVC" framework. "MVC", however, is
- close enough as a general classification moniker for purposes of
- comparison with other web frameworks.
+Also unlike :term:`Zope` and other "full-stack" frameworks such as
+:term:`Django`, :app:`Pyramid` makes no assumptions about which persistence
+mechanisms you should use to build an application. Zope applications are
+typically reliant on :term:`ZODB`. :app:`Pyramid` allows you to build
+:term:`ZODB` applications, but it has no reliance on the ZODB software.
+Likewise, :term:`Django` tends to assume that you want to store your
+application's data in a relational database. :app:`Pyramid` makes no such
+assumption, allowing you to use a relational database, and neither encouraging
+nor discouraging the decision.
+
+Other Python web frameworks advertise themselves as members of a class of web
+frameworks named `model-view-controller
+<http://en.wikipedia.org/wiki/Model–view–controller>`_ frameworks. Insofar as
+this term has been claimed to represent a class of web frameworks,
+:app:`Pyramid` also generally fits into this class.
+
+.. sidebar:: You Say :app:`Pyramid` is MVC, but Where's the Controller?
+
+ The :app:`Pyramid` authors believe that the MVC pattern just doesn't really
+ fit the web very well. In a :app:`Pyramid` application, there is a resource
+ tree which represents the site structure, and views which tend to present
+ the data stored in the resource tree and a user-defined "domain model".
+ However, no facility provided *by the framework* actually necessarily maps
+ to the concept of a "controller" or "model". So if you had to give it some
+ acronym, I guess you'd say :app:`Pyramid` is actually an "RV" framework
+ rather than an "MVC" framework. "MVC", however, is close enough as a
+ general classification moniker for purposes of comparison with other web
+ frameworks.
diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst
index 921883091..9c6e8a319 100644
--- a/docs/narr/logging.rst
+++ b/docs/narr/logging.rst
@@ -4,17 +4,17 @@ Logging
=======
:app:`Pyramid` allows you to make use of the Python standard library
-:mod:`logging` module. This chapter describes how to configure logging and
-how to send log messages to loggers that you've configured.
+:mod:`logging` module. This chapter describes how to configure logging and how
+to send log messages to loggers that you've configured.
.. warning::
- This chapter assumes you've used a :term:`scaffold` to create a
- project which contains ``development.ini`` and ``production.ini`` files
- which help configure logging. All of the scaffolds which ship along with
- :app:`Pyramid` do this. If you're not using a scaffold, or if you've used
- a third-party scaffold which does not create these files, the
- configuration information in this chapter may not be applicable.
+ This chapter assumes you've used a :term:`scaffold` to create a project
+ which contains ``development.ini`` and ``production.ini`` files which help
+ configure logging. All of the scaffolds which ship with :app:`Pyramid` do
+ this. If you're not using a scaffold, or if you've used a third-party
+ scaffold which does not create these files, the configuration information in
+ this chapter may not be applicable.
.. index:
pair: settings; logging
@@ -26,30 +26,29 @@ how to send log messages to loggers that you've configured.
Logging Configuration
---------------------
-A :app:`Pyramid` project created from a :term:`scaffold` is configured to
-allow you to send messages to :mod:`Python standard library logging package
-<logging>` loggers from within your
-application. In particular, the :term:`PasteDeploy` ``development.ini`` and
-``production.ini`` files created when you use a scaffold include a basic
-configuration for the Python :mod:`logging` package.
+A :app:`Pyramid` project created from a :term:`scaffold` is configured to allow
+you to send messages to :mod:`Python standard library logging package
+<logging>` loggers from within your application. In particular, the
+:term:`PasteDeploy` ``development.ini`` and ``production.ini`` files created
+when you use a scaffold include a basic configuration for the Python
+:mod:`logging` package.
PasteDeploy ``.ini`` files use the Python standard library :mod:`ConfigParser
-format <ConfigParser>`; this is the same format used as the Python
+format <ConfigParser>`. This is the same format used as the Python
:ref:`logging module's Configuration file format <logging-config-fileformat>`.
The application-related and logging-related sections in the configuration file
can coexist peacefully, and the logging-related sections in the file are used
from when you run ``pserve``.
-The ``pserve`` command calls the :func:`pyramid.paster.setup_logging`
-function, a thin wrapper around the :func:`logging.config.fileConfig`
-using the specified ``.ini`` file if it contains a ``[loggers]`` section
-(all of the scaffold-generated ``.ini`` files do). ``setup_logging`` reads the
-logging configuration from the ini file upon which ``pserve`` was
-invoked.
+The ``pserve`` command calls the :func:`pyramid.paster.setup_logging` function,
+a thin wrapper around the :func:`logging.config.fileConfig` using the specified
+``.ini`` file, if it contains a ``[loggers]`` section (all of the
+scaffold-generated ``.ini`` files do). ``setup_logging`` reads the logging
+configuration from the ini file upon which ``pserve`` was invoked.
Default logging configuration is provided in both the default
-``development.ini`` and the ``production.ini`` file. The logging
-configuration in the ``development.ini`` file is as follows:
+``development.ini`` and the ``production.ini`` file. The logging configuration
+in the ``development.ini`` file is as follows:
.. code-block:: ini
:linenos:
@@ -135,46 +134,44 @@ The logging configuration will literally be:
In this logging configuration:
-- a logger named ``root`` is created that logs messages at a level above
- or equal to the ``INFO`` level to stderr, with the following format:
+- a logger named ``root`` is created that logs messages at a level above or
+ equal to the ``INFO`` level to stderr, with the following format:
- .. code-block:: text
+ .. code-block:: text
- 2007-08-17 15:04:08,704 INFO [packagename]
- Loading resource, id: 86
+ 2007-08-17 15:04:08,704 INFO [packagename] Loading resource, id: 86
- a logger named ``myapp`` is configured that logs messages sent at a level
- above or equal to ``DEBUG`` to stderr in the same format as the root
- logger.
+ above or equal to ``DEBUG`` to stderr in the same format as the root logger.
The ``root`` logger will be used by all applications in the Pyramid process
-that ask for a logger (via ``logging.getLogger``) that has a name which
-begins with anything except your project's package name (e.g. ``myapp``).
-The logger with the same name as your package name is reserved for your own
-usage in your Pyramid application. Its existence means that you can log to a
-known logging location from any Pyramid application generated via a scaffold.
-
-Pyramid and many other libraries (such as Beaker, SQLAlchemy, Paste) log a
-number of messages to the root logger for debugging purposes. Switching the
+that ask for a logger (via ``logging.getLogger``) that has a name which begins
+with anything except your project's package name (e.g., ``myapp``). The logger
+with the same name as your package name is reserved for your own usage in your
+:app:`Pyramid` application. Its existence means that you can log to a known
+logging location from any :app:`Pyramid` application generated via a scaffold.
+
+:app:`Pyramid` and many other libraries (such as Beaker, SQLAlchemy, Paste) log
+a number of messages to the root logger for debugging purposes. Switching the
root logger level to ``DEBUG`` reveals them:
-.. code-block:: ini
+.. code-block:: ini
- [logger_root]
- #level = INFO
- level = DEBUG
- handlers = console
+ [logger_root]
+ #level = INFO
+ level = DEBUG
+ handlers = console
-Some scaffolds configure additional loggers for additional subsystems they
-use (such as SQLALchemy). Take a look at the ``production.ini`` and
+Some scaffolds configure additional loggers for additional subsystems they use
+(such as SQLALchemy). Take a look at the ``production.ini`` and
``development.ini`` files rendered when you create a project from a scaffold.
Sending Logging Messages
------------------------
Python's special ``__name__`` variable refers to the current module's fully
-qualified name. From any module in a package named ``myapp``, the
-``__name__`` builtin variable will always be something like ``myapp``, or
+qualified name. From any module in a package named ``myapp``, the ``__name__``
+builtin variable will always be something like ``myapp``, or
``myapp.subpackage`` or ``myapp.package.subpackage`` if your project is named
``myapp``. Sending a message to this logger will send it to the ``myapp``
logger.
@@ -183,75 +180,75 @@ To log messages to the package-specific logger configured in your ``.ini``
file, simply create a logger object using the ``__name__`` builtin and call
methods on it.
-.. code-block:: python
+.. code-block:: python
:linenos:
- import logging
- log = logging.getLogger(__name__)
+ import logging
+ log = logging.getLogger(__name__)
def myview(request):
- content_type = 'text/plain'
- content = 'Hello World!'
- log.debug('Returning: %s (content-type: %s)', content, content_type)
- request.response.content_type = content_type
+ content_type = 'text/plain'
+ content = 'Hello World!'
+ log.debug('Returning: %s (content-type: %s)', content, content_type)
+ request.response.content_type = content_type
return request.response
-This will result in the following printed to the console, on ``stderr``:
+This will result in the following printed to the console, on ``stderr``:
-.. code-block:: text
+.. code-block:: text
16:20:20,440 DEBUG [myapp.views] Returning: Hello World!
- (content-type: text/plain)
+ (content-type: text/plain)
Filtering log messages
----------------------
-Often there's too much log output to sift through, such as when switching
-the root logger's level to ``DEBUG``.
+Often there's too much log output to sift through, such as when switching the
+root logger's level to ``DEBUG``.
-An example: you're diagnosing database connection issues in your application
+For example, you're diagnosing database connection issues in your application
and only want to see SQLAlchemy's ``DEBUG`` messages in relation to database
connection pooling. You can leave the root logger's level at the less verbose
``INFO`` level and set that particular SQLAlchemy logger to ``DEBUG`` on its
own, apart from the root logger:
-.. code-block:: ini
+.. code-block:: ini
- [logger_sqlalchemy.pool]
- level = DEBUG
- handlers =
- qualname = sqlalchemy.pool
+ [logger_sqlalchemy.pool]
+ level = DEBUG
+ handlers =
+ qualname = sqlalchemy.pool
-then add it to the list of loggers:
+then add it to the list of loggers:
-.. code-block:: ini
+.. code-block:: ini
- [loggers]
- keys = root, myapp, sqlalchemy.pool
+ [loggers]
+ keys = root, myapp, sqlalchemy.pool
-No handlers need to be configured for this logger as by default non root
-loggers will propagate their log records up to their parent logger's
-handlers. The root logger is the top level parent of all loggers.
+No handlers need to be configured for this logger as by default non-root
+loggers will propagate their log records up to their parent logger's handlers.
+The root logger is the top level parent of all loggers.
This technique is used in the default ``development.ini``. The root logger's
level is set to ``INFO``, whereas the application's log level is set to
``DEBUG``:
-.. code-block:: ini
+.. code-block:: ini
- # Begin logging configuration
+ # Begin logging configuration
- [loggers]
+ [loggers]
keys = root, myapp
- [logger_myapp]
- level = DEBUG
- handlers =
- qualname = myapp
+ [logger_myapp]
+ level = DEBUG
+ handlers =
+ qualname = myapp
All of the child loggers of the ``myapp`` logger will inherit the ``DEBUG``
level unless they're explicitly set differently. Meaning the ``myapp.views``,
-``myapp.models`` (and all your app's modules') loggers by default have an
+``myapp.models``, and all your app's modules' loggers by default have an
effective level of ``DEBUG`` too.
For more advanced filtering, the logging module provides a
@@ -264,39 +261,38 @@ Advanced Configuration
To capture log output to a separate file, use :class:`logging.FileHandler` (or
:class:`logging.handlers.RotatingFileHandler`):
-.. code-block:: ini
+.. code-block:: ini
- [handler_filelog]
- class = FileHandler
- args = ('%(here)s/myapp.log','a')
- level = INFO
- formatter = generic
+ [handler_filelog]
+ class = FileHandler
+ args = ('%(here)s/myapp.log','a')
+ level = INFO
+ formatter = generic
-Before it's recognized, it needs to be added to the list of handlers:
+Before it's recognized, it needs to be added to the list of handlers:
-.. code-block:: ini
+.. code-block:: ini
- [handlers]
+ [handlers]
keys = console, myapp, filelog
-and finally utilized by a logger.
+and finally utilized by a logger.
-.. code-block:: ini
+.. code-block:: ini
- [logger_root]
- level = INFO
+ [logger_root]
+ level = INFO
handlers = console, filelog
-These final 3 lines of configuration directs all of the root logger's output
+These final three lines of configuration direct all of the root logger's output
to the ``myapp.log`` as well as the console.
Logging Exceptions
------------------
-To log (or email) exceptions generated by your :app:`Pyramid` application,
-use the :term:`pyramid_exclog` package. Details about its configuration are
-in its `documentation
-<http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_.
+To log or email exceptions generated by your :app:`Pyramid` application, use
+the :term:`pyramid_exclog` package. Details about its configuration are in its
+`documentation <http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_.
.. index::
single: TransLogger
@@ -307,40 +303,40 @@ in its `documentation
.. _request_logging_with_pastes_translogger:
-Request Logging with Paste's TransLogger
+Request Logging with Paste's TransLogger
----------------------------------------
-The term:`WSGI` design is modular. Waitress logs error conditions, debugging
-output, etc., but not web traffic. For web traffic logging Paste provides the
+The :term:`WSGI` design is modular. Waitress logs error conditions, debugging
+output, etc., but not web traffic. For web traffic logging, Paste provides the
`TransLogger <http://pythonpaste.org/modules/translogger.html>`_
:term:`middleware`. TransLogger produces logs in the `Apache Combined Log
Format <http://httpd.apache.org/docs/2.2/logs.html#combined>`_. But
-TransLogger does not write to files, the Python logging system must be
-configured to do this. The Python :class:`logging.FileHandler` logging
-handler can be used alongside TransLogger to create an ``access.log`` file
-similar to Apache's.
+TransLogger does not write to files; the Python logging system must be
+configured to do this. The Python :class:`logging.FileHandler` logging handler
+can be used alongside TransLogger to create an ``access.log`` file similar to
+Apache's.
Like any standard :term:`middleware` with a Paste entry point, TransLogger can
-be configured to wrap your application using ``.ini`` file syntax. First,
+be configured to wrap your application using ``.ini`` file syntax. First
rename your Pyramid ``.ini`` file's ``[app:main]`` section to
-``[app:mypyramidapp]``, then add a ``[filter:translogger]`` section, then use
-a ``[pipeline:main]`` section file to form a WSGI pipeline with both the
+``[app:mypyramidapp]``, then add a ``[filter:translogger]`` section, then use a
+``[pipeline:main]`` section file to form a WSGI pipeline with both the
translogger and your application in it. For instance, change from this:
-.. code-block:: ini
+.. code-block:: ini
[app:main]
use = egg:MyProject
To this:
-.. code-block:: ini
+.. code-block:: ini
[app:mypyramidapp]
use = egg:MyProject
- [filter:translogger]
- use = egg:Paste#translogger
+ [filter:translogger]
+ use = egg:Paste#translogger
setup_console_handler = False
[pipeline:main]
@@ -351,41 +347,40 @@ Using PasteDeploy this way to form and serve a pipeline is equivalent to
wrapping your app in a TransLogger instance via the bottom of the ``main``
function of your project's ``__init__`` file:
-.. code-block:: python
+.. code-block:: python
...
app = config.make_wsgi_app()
- from paste.translogger import TransLogger
- app = TransLogger(app, setup_console_handler=False)
- return app
-
+ from paste.translogger import TransLogger
+ app = TransLogger(app, setup_console_handler=False)
+ return app
.. note::
TransLogger will automatically setup a logging handler to the console when
- called with no arguments, so it 'just works' in environments that don't
- configure logging. Since our logging handlers are configured we disable
+ called with no arguments, so it "just works" in environments that don't
+ configure logging. Since our logging handlers are configured, we disable
the automation via ``setup_console_handler = False``.
With the filter in place, TransLogger's logger (named the ``wsgi`` logger) will
-propagate its log messages to the parent logger (the root logger), sending
-its output to the console when we request a page:
+propagate its log messages to the parent logger (the root logger), sending its
+output to the console when we request a page:
-.. code-block:: text
+.. code-block:: text
00:50:53,694 INFO [myapp.views] Returning: Hello World!
- (content-type: text/plain)
+ (content-type: text/plain)
00:50:53,695 INFO [wsgi] 192.168.1.111 - - [11/Aug/2011:20:09:33 -0700] "GET /hello
- HTTP/1.1" 404 - "-"
+ HTTP/1.1" 404 - "-"
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.6) Gecko/20070725
- Firefox/2.0.0.6"
+ Firefox/2.0.0.6"
To direct TransLogger to an ``access.log`` FileHandler, we need the following
to add a FileHandler (named ``accesslog``) to the list of handlers, and ensure
that the ``wsgi`` logger is configured and uses this handler accordingly:
-.. code-block:: ini
+.. code-block:: ini
- # Begin logging configuration
+ # Begin logging configuration
[loggers]
keys = root, myapp, wsgi
@@ -393,44 +388,43 @@ that the ``wsgi`` logger is configured and uses this handler accordingly:
[handlers]
keys = console, accesslog
- [logger_wsgi]
- level = INFO
+ [logger_wsgi]
+ level = INFO
handlers = accesslog
- qualname = wsgi
- propagate = 0
-
- [handler_accesslog]
- class = FileHandler
- args = ('%(here)s/access.log','a')
- level = INFO
- formatter = generic
-
-As mentioned above, non-root loggers by default propagate their log records
-to the root logger's handlers (currently the console handler). Setting
-``propagate`` to ``0`` (``False``) here disables this; so the ``wsgi`` logger
+ qualname = wsgi
+ propagate = 0
+
+ [handler_accesslog]
+ class = FileHandler
+ args = ('%(here)s/access.log','a')
+ level = INFO
+ formatter = generic
+
+As mentioned above, non-root loggers by default propagate their log records to
+the root logger's handlers (currently the console handler). Setting
+``propagate`` to ``0`` (``False``) here disables this; so the ``wsgi`` logger
directs its records only to the ``accesslog`` handler.
Finally, there's no need to use the ``generic`` formatter with TransLogger as
-TransLogger itself provides all the information we need. We'll use a
-formatter that passes-through the log messages as is. Add a new formatter
-called ``accesslog`` by including the following in your configuration file:
-
-.. code-block:: ini
+TransLogger itself provides all the information we need. We'll use a formatter
+that passes through the log messages as is. Add a new formatter called
+``accesslog`` by including the following in your configuration file:
- [formatters]
- keys = generic, accesslog
+.. code-block:: ini
- [formatter_accesslog]
- format = %(message)s
+ [formatters]
+ keys = generic, accesslog
+ [formatter_accesslog]
+ format = %(message)s
-Finally alter the existing configuration to wire this new
-``accesslog`` formatter into the FileHandler:
+Finally alter the existing configuration to wire this new ``accesslog``
+formatter into the FileHandler:
-.. code-block:: ini
+.. code-block:: ini
- [handler_accesslog]
- class = FileHandler
- args = ('%(here)s/access.log','a')
- level = INFO
- formatter = accesslog
+ [handler_accesslog]
+ class = FileHandler
+ args = ('%(here)s/access.log','a')
+ level = INFO
+ formatter = accesslog
diff --git a/docs/narr/paste.rst b/docs/narr/paste.rst
index f1fb70869..0a217e6e3 100644
--- a/docs/narr/paste.rst
+++ b/docs/narr/paste.rst
@@ -7,54 +7,54 @@ Packages generated via a :term:`scaffold` make use of a system created by Ian
Bicking named :term:`PasteDeploy`. PasteDeploy defines a way to declare
:term:`WSGI` application configuration in an ``.ini`` file.
-Pyramid uses this configuration file format in input to its :term:`WSGI`
-server runner ``pserve``, as well as other commands such as ``pviews``,
-``pshell``, ``proutes``, and ``ptweens``.
+Pyramid uses this configuration file format as input to its :term:`WSGI` server
+runner ``pserve``, as well as other commands such as ``pviews``, ``pshell``,
+``proutes``, and ``ptweens``.
PasteDeploy is not a particularly integral part of Pyramid. It's possible to
-create a Pyramid application which does not use PasteDeploy at all. We show
-a Pyramid application that doesn't use PasteDeploy in
-:ref:`firstapp_chapter`. However, all Pyramid scaffolds render PasteDeploy
-configuration files, to provide new developers with a standardized way of
-setting deployment values, and to provide new users with a standardized way
-of starting, stopping, and debugging an application.
-
-This chapter is not a replacement for documentation about PasteDeploy; it
-only contextualizes the use of PasteDeploy within Pyramid. For detailed
+create a Pyramid application which does not use PasteDeploy at all. We show a
+Pyramid application that doesn't use PasteDeploy in :ref:`firstapp_chapter`.
+However, all Pyramid scaffolds render PasteDeploy configuration files, to
+provide new developers with a standardized way of setting deployment values,
+and to provide new users with a standardized way of starting, stopping, and
+debugging an application.
+
+This chapter is not a replacement for documentation about PasteDeploy; it only
+contextualizes the use of PasteDeploy within Pyramid. For detailed
documentation, see http://pythonpaste.org/deploy/.
PasteDeploy
-----------
-:term:`PasteDeploy` is the system that Pyramid uses to allow
-:term:`deployment settings` to be spelled using an ``.ini`` configuration
-file format. It also allows the ``pserve`` command to work. Its
-configuration format provides a convenient place to define application
-:term:`deployment settings` and WSGI server settings, and its server runner
-allows you to stop and start a Pyramid application easily.
+:term:`PasteDeploy` is the system that Pyramid uses to allow :term:`deployment
+settings` to be specified using an ``.ini`` configuration file format. It also
+allows the ``pserve`` command to work. Its configuration format provides a
+convenient place to define application :term:`deployment settings` and WSGI
+server settings, and its server runner allows you to stop and start a Pyramid
+application easily.
.. _pastedeploy_entry_points:
Entry Points and PasteDeploy ``.ini`` Files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the :ref:`project_narr` chapter, we breezed over the meaning of a
configuration line in the ``deployment.ini`` file. This was the ``use =
-egg:MyProject`` line in the ``[app:main]`` section. We breezed over it
-because it's pretty confusing and "too much information" for an introduction
-to the system. We'll try to give it a bit of attention here. Let's see the
-config file again:
+egg:MyProject`` line in the ``[app:main]`` section. We breezed over it because
+it's pretty confusing and "too much information" for an introduction to the
+system. We'll try to give it a bit of attention here. Let's see the config
+file again:
.. literalinclude:: MyProject/development.ini
:language: ini
:linenos:
-The line in ``[app:main]`` above that says ``use = egg:MyProject`` is
-actually shorthand for a longer spelling: ``use = egg:MyProject#main``. The
-``#main`` part is omitted for brevity, as ``#main`` is a default defined by
-PasteDeploy. ``egg:MyProject#main`` is a string which has meaning to
-PasteDeploy. It points at a :term:`setuptools` :term:`entry point` named
-``main`` defined in the ``MyProject`` project.
+The line in ``[app:main]`` above that says ``use = egg:MyProject`` is actually
+shorthand for a longer spelling: ``use = egg:MyProject#main``. The ``#main``
+part is omitted for brevity, as ``#main`` is a default defined by PasteDeploy.
+``egg:MyProject#main`` is a string which has meaning to PasteDeploy. It points
+at a :term:`setuptools` :term:`entry point` named ``main`` defined in the
+``MyProject`` project.
Take a look at the generated ``setup.py`` file for this project.
@@ -62,19 +62,19 @@ Take a look at the generated ``setup.py`` file for this project.
:language: python
:linenos:
-Note that ``entry_points`` is assigned a string which
-looks a lot like an ``.ini`` file. This string representation of an ``.ini``
-file has a section named ``[paste.app_factory]``. Within this section, there
-is a key named ``main`` (the entry point name) which has a value
-``myproject:main``. The *key* ``main`` is what our ``egg:MyProject#main``
-value of the ``use`` section in our config file is pointing at, although it
-is actually shortened to ``egg:MyProject`` there. The value represents a
-:term:`dotted Python name` path, which refers to a callable in our
-``myproject`` package's ``__init__.py`` module.
+Note that ``entry_points`` is assigned a string which looks a lot like an
+``.ini`` file. This string representation of an ``.ini`` file has a section
+named ``[paste.app_factory]``. Within this section, there is a key named
+``main`` (the entry point name) which has a value ``myproject:main``. The
+*key* ``main`` is what our ``egg:MyProject#main`` value of the ``use`` section
+in our config file is pointing at, although it is actually shortened to
+``egg:MyProject`` there. The value represents a :term:`dotted Python name`
+path, which refers to a callable in our ``myproject`` package's ``__init__.py``
+module.
-The ``egg:`` prefix in ``egg:MyProject`` indicates that this is an entry
-point *URI* specifier, where the "scheme" is "egg". An "egg" is created when
-you run ``setup.py install`` or ``setup.py develop`` within your project.
+The ``egg:`` prefix in ``egg:MyProject`` indicates that this is an entry point
+*URI* specifier, where the "scheme" is "egg". An "egg" is created when you run
+``setup.py install`` or ``setup.py develop`` within your project.
In English, this entry point can thus be referred to as a "PasteDeploy
application factory in the ``MyProject`` project which has the entry point
@@ -88,13 +88,11 @@ configuration object and *returns* an instance of our application.
.. _defaults_section_of_pastedeploy_file:
``[DEFAULT]`` Section of a PasteDeploy ``.ini`` File
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-You can add a ``[DEFAULT]`` section to your PasteDeploy ``.ini`` file. Such
-a section should consists of global parameters that are shared by all the
-applications, servers and :term:`middleware` defined within the configuration
+You can add a ``[DEFAULT]`` section to your PasteDeploy ``.ini`` file. Such a
+section should consist of global parameters that are shared by all the
+applications, servers, and :term:`middleware` defined within the configuration
file. The values in a ``[DEFAULT]`` section will be passed to your
-application's ``main`` function as ``global_config`` (see the reference to
-the ``main`` function in :ref:`init_py`).
-
-
+application's ``main`` function as ``global_config`` (see the reference to the
+``main`` function in :ref:`init_py`).
diff --git a/docs/narr/project-debug.png b/docs/narr/project-debug.png
index 4f8e441ef..0a703dead 100644
--- a/docs/narr/project-debug.png
+++ b/docs/narr/project-debug.png
Binary files differ
diff --git a/docs/narr/project-show-toolbar.png b/docs/narr/project-show-toolbar.png
new file mode 100644
index 000000000..89b838f64
--- /dev/null
+++ b/docs/narr/project-show-toolbar.png
Binary files differ
diff --git a/docs/narr/project.png b/docs/narr/project.png
index 5d46df0dd..e1afd97d4 100644
--- a/docs/narr/project.png
+++ b/docs/narr/project.png
Binary files differ
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 0ada1a379..25f3931e9 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -1,27 +1,26 @@
.. _project_narr:
Creating a :app:`Pyramid` Project
-====================================
+=================================
-As we saw in :ref:`firstapp_chapter`, it's possible to create a
-:app:`Pyramid` application completely manually. However, it's usually more
-convenient to use a :term:`scaffold` to generate a basic :app:`Pyramid`
-:term:`project`.
+As we saw in :ref:`firstapp_chapter`, it's possible to create a :app:`Pyramid`
+application completely manually. However, it's usually more convenient to use
+a :term:`scaffold` to generate a basic :app:`Pyramid` :term:`project`.
A project is a directory that contains at least one Python :term:`package`.
You'll use a scaffold to create a project, and you'll create your application
-logic within a package that lives inside the project. Even if your
-application is extremely simple, it is useful to place code that drives the
-application within a package, because: 1) a package is more easily extended
-with new code and 2) an application that lives inside a package can also be
-distributed more easily than one which does not live within a package.
+logic within a package that lives inside the project. Even if your application
+is extremely simple, it is useful to place code that drives the application
+within a package, because (1) a package is more easily extended with new code,
+and (2) an application that lives inside a package can also be distributed more
+easily than one which does not live within a package.
-:app:`Pyramid` comes with a variety of scaffolds that you can use to generate
-a project. Each scaffold makes different configuration assumptions about
-what type of application you're trying to construct.
+:app:`Pyramid` comes with a variety of scaffolds that you can use to generate a
+project. Each scaffold makes different configuration assumptions about what
+type of application you're trying to construct.
-These scaffolds are rendered using the ``pcreate`` command that is installed
-as part of Pyramid.
+These scaffolds are rendered using the ``pcreate`` command that is installed as
+part of Pyramid.
.. index::
single: scaffolds
@@ -32,28 +31,27 @@ as part of Pyramid.
.. _additional_paster_scaffolds:
Scaffolds Included with :app:`Pyramid`
-------------------------------------------------
+--------------------------------------
-The convenience scaffolds included with :app:`Pyramid` differ from
-each other on a number of axes:
+The convenience scaffolds included with :app:`Pyramid` differ from each other
+on a number of axes:
-- the persistence mechanism they offer (no persistence mechanism,
- :term:`ZODB`, or :term:`SQLAlchemy`).
+- the persistence mechanism they offer (no persistence mechanism, :term:`ZODB`,
+ or :term:`SQLAlchemy`)
- the mechanism they use to map URLs to code (:term:`traversal` or :term:`URL
- dispatch`).
+ dispatch`)
The included scaffolds are these:
``starter``
- URL mapping via :term:`URL dispatch` and no persistence mechanism.
+ URL mapping via :term:`URL dispatch` and no persistence mechanism
``zodb``
- URL mapping via :term:`traversal` and persistence via :term:`ZODB`.
+ URL mapping via :term:`traversal` and persistence via :term:`ZODB`
``alchemy``
- URL mapping via :term:`URL dispatch` and persistence via
- :term:`SQLAlchemy`
+ URL mapping via :term:`URL dispatch` and persistence via :term:`SQLAlchemy`
.. index::
single: creating a project
@@ -64,13 +62,13 @@ The included scaffolds are these:
Creating the Project
--------------------
-In :ref:`installing_chapter`, you created a virtual Python environment via
-the ``virtualenv`` command. To start a :app:`Pyramid` :term:`project`, use
-the ``pcreate`` command installed within the virtualenv. We'll choose the
+In :ref:`installing_chapter`, you created a virtual Python environment via the
+``virtualenv`` command. To start a :app:`Pyramid` :term:`project`, use the
+``pcreate`` command installed within the virtualenv. We'll choose the
``starter`` scaffold for this purpose. When we invoke ``pcreate``, it will
create a directory that represents our project.
-In :ref:`installing_chapter` we called the virtualenv directory ``env``; the
+In :ref:`installing_chapter` we called the virtualenv directory ``env``. The
following commands assume that our current working directory is the ``env``
directory.
@@ -89,7 +87,6 @@ Or on Windows:
> %VENV%\Scripts\pcreate -s starter MyProject
-
Here's sample output from a run of ``pcreate`` on UNIX for a project we name
``MyProject``:
@@ -102,14 +99,14 @@ Here's sample output from a run of ``pcreate`` on UNIX for a project we name
Running /Users/chrism/projects/pyramid/bin/python setup.py egg_info
As a result of invoking the ``pcreate`` command, a directory named
-``MyProject`` is created. That directory is a :term:`project` directory.
-The ``setup.py`` file in that directory can be used to distribute your
-application, or install your application for deployment or development.
+``MyProject`` is created. That directory is a :term:`project` directory. The
+``setup.py`` file in that directory can be used to distribute your application,
+or install your application for deployment or development.
A ``.ini`` file named ``development.ini`` will be created in the project
-directory. You will use this ``.ini`` file to configure a server, to run
-your application, and to debug your application. It contains configuration
-that enables an interactive debugger and settings optimized for development.
+directory. You will use this ``.ini`` file to configure a server, to run your
+application, and to debug your application. It contains configuration that
+enables an interactive debugger and settings optimized for development.
Another ``.ini`` file named ``production.ini`` will also be created in the
project directory. It contains configuration that disables any interactive
@@ -118,28 +115,28 @@ number of debugging settings. You can use this file to put your application
into production.
The ``MyProject`` project directory contains an additional subdirectory named
-``myproject`` (note the case difference) representing a Python
-:term:`package` which holds very simple :app:`Pyramid` sample code. This is
-where you'll edit your application's Python code and templates.
-
-We created this project within an ``env`` virtualenv directory. However,
-note that this is not mandatory. The project directory can go more or less
-anywhere on your filesystem. You don't need to put it in a special "web
-server" directory, and you don't need to put it within a virtualenv
-directory. The author uses Linux mainly, and tends to put project
-directories which he creates within his ``~/projects`` directory. On
-Windows, it's a good idea to put project directories within a directory that
-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``.
+``myproject`` (note the case difference) representing a Python :term:`package`
+which holds very simple :app:`Pyramid` sample code. This is where you'll edit
+your application's Python code and templates.
+
+We created this project within an ``env`` virtualenv directory. However, note
+that this is not mandatory. The project directory can go more or less anywhere
+on your filesystem. You don't need to put it in a special "web server"
+directory, and you don't need to put it within a virtualenv directory. The
+author uses Linux mainly, and tends to put project directories which he creates
+within his ``~/projects`` directory. On Windows, it's a good idea to put
+project directories within a directory that 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::
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
- using the name ``pyramid``, which will conflict with Pyramid itself.
+ should avoid using the names ``site`` or ``test``, both of which conflict
+ with Python standard library packages. You should also avoid using the name
+ ``pyramid``, which will conflict with Pyramid itself.
.. index::
single: setup.py develop
@@ -154,10 +151,10 @@ newly created project directory and use the Python interpreter from the
command ``python setup.py develop``
The file named ``setup.py`` will be in the root of the pcreate-generated
-project directory. The ``python`` you're invoking should be the one that
-lives in the ``bin`` (or ``Scripts`` on Windows) directory of your virtual
-Python environment. Your terminal's current working directory *must* be the
-newly created project directory.
+project directory. The ``python`` you're invoking should be the one that lives
+in the ``bin`` (or ``Scripts`` on Windows) directory of your virtual Python
+environment. Your terminal's current working directory *must* be the newly
+created project directory.
On UNIX:
@@ -182,20 +179,20 @@ 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 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``.
+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
single: tests (running)
-Running The Tests For Your Application
+Running the Tests for Your Application
--------------------------------------
-To run unit tests for your application, you should invoke them using the
-Python interpreter from the :term:`virtualenv` you created during
+To run unit tests for your application, you should invoke them using the Python
+interpreter from the :term:`virtualenv` you created during
:ref:`installing_chapter` (the ``python`` command that lives in the ``bin``
directory of your virtualenv).
@@ -250,7 +247,7 @@ single sample test exists.
.. _running_the_project_application:
-Running The Project Application
+Running the Project Application
-------------------------------
Once a project is installed for development, you can run the application it
@@ -279,16 +276,16 @@ Here's sample output from a run of ``pserve`` on UNIX:
When you use ``pserve`` to start the application implied by the default
rendering of a scaffold, it will respond to requests on *all* IP addresses
-possessed by your system, not just requests to ``localhost``. This is what
-the ``0.0.0.0`` in ``serving on http://0.0.0.0:6543`` means. The server will
-respond to requests made to ``127.0.0.1`` and on any external IP address.
-For example, your system might be configured to have an external IP address
-``192.168.1.50``. If that's the case, if you use a browser running on the
-same system as Pyramid, it will be able to access the application via
-``http://127.0.0.1:6543/`` as well as via
-``http://192.168.1.50:6543/``. However, *other people* on other computers on
-the same network will also be able to visit your Pyramid application in their
-browser by visiting ``http://192.168.1.50:6543/``.
+possessed by your system, not just requests to ``localhost``. This is what the
+``0.0.0.0`` in ``serving on http://0.0.0.0:6543`` means. The server will
+respond to requests made to ``127.0.0.1`` and on any external IP address. For
+example, your system might be configured to have an external IP address
+``192.168.1.50``. If that's the case, if you use a browser running on the same
+system as Pyramid, it will be able to access the application via
+``http://127.0.0.1:6543/`` as well as via ``http://192.168.1.50:6543/``.
+However, *other people* on other computers on the same network will also be
+able to visit your Pyramid application in their browser by visiting
+``http://192.168.1.50:6543/``.
If you want to restrict access such that only a browser running on the same
machine as Pyramid will be able to access your Pyramid application, edit the
@@ -306,27 +303,26 @@ example:
You can change the port on which the server runs on by changing the same
portion of the ``development.ini`` file. For example, you can change the
``port = 6543`` line in the ``development.ini`` file's ``[server:main]``
-section to ``port = 8080`` to run the server on port 8080 instead of
-port 6543.
+section to ``port = 8080`` to run the server on port 8080 instead of port 6543.
-You can shut down a server started this way by pressing ``Ctrl-C``.
+You can shut down a server started this way by pressing ``Ctrl-C`` (or
+``Ctrl-Break`` on Windows).
The default server used to run your Pyramid application when a project is
-created from a scaffold is named :term:`Waitress`. This server is what
-prints the ``serving on...`` line when you run ``pserve``. It's a good idea
-to use this server during development, because it's very simple. It can also
-be used for light production. Setting your application up under a different
-server is not advised until you've done some development work under the
-default server, particularly if you're not yet experienced with Python web
-development. Python web server setup can be complex, and you should get some
-confidence that your application works in a default environment before trying
-to optimize it or make it "more like production". It's awfully easy to get
-sidetracked trying to set up a nondefault server for hours without actually
-starting to do any development. One of the nice things about Python web
-servers is that they're largely interchangeable, so if your application works
-under the default server, it will almost certainly work under any other
-server in production if you eventually choose to use a different one. Don't
-worry about it right now.
+created from a scaffold is named :term:`Waitress`. This server is what prints
+the ``serving on...`` line when you run ``pserve``. It's a good idea to use
+this server during development because it's very simple. It can also be used
+for light production. Setting your application up under a different server is
+not advised until you've done some development work under the default server,
+particularly if you're not yet experienced with Python web development. Python
+web server setup can be complex, and you should get some confidence that your
+application works in a default environment before trying to optimize it or make
+it "more like production". It's awfully easy to get sidetracked trying to set
+up a non-default server for hours without actually starting to do any
+development. One of the nice things about Python web servers is that they're
+largely interchangeable, so if your application works under the default server,
+it will almost certainly work under any other server in production if you
+eventually choose to use a different one. Don't worry about it right now.
For more detailed information about the startup process, see
:ref:`startup_chapter`. For more information about environment variables and
@@ -338,10 +334,10 @@ configuration file settings that influence startup and runtime behavior, see
Reloading Code
~~~~~~~~~~~~~~
-During development, it's often useful to run ``pserve`` using its
-``--reload`` option. When ``--reload`` is passed to ``pserve``, changes to
-any Python module your project uses will cause the server to restart. This
-typically makes development easier, as changes to Python code made within a
+During development, it's often useful to run ``pserve`` using its ``--reload``
+option. When ``--reload`` is passed to ``pserve``, changes to any Python
+module your project uses will cause the server to restart. This typically
+makes development easier, as changes to Python code made within a
:app:`Pyramid` application is not put into effect until the server restarts.
For example, on UNIX:
@@ -364,10 +360,10 @@ files, you'll see the server restart automatically:
serving on http://0.0.0.0:6543
Changes to template files (such as ``.pt`` or ``.mak`` files) won't cause the
-server to restart. Changes to template files don't require a server restart
-as long as the ``pyramid.reload_templates`` setting in the
-``development.ini`` file is ``true``. Changes made to template files when
-this setting is true will take effect immediately without a server restart.
+server to restart. Changes to template files don't require a server restart as
+long as the ``pyramid.reload_templates`` setting in the ``development.ini``
+file is ``true``. Changes made to template files when this setting is true
+will take effect immediately without a server restart.
.. index::
single: WSGI
@@ -392,25 +388,25 @@ generated ``starter`` application in a browser.
The Debug Toolbar
~~~~~~~~~~~~~~~~~
-If you click on the image shown at the right hand top of the page ("^DT"),
-you'll be presented with a debug toolbar that provides various niceties while
-you're developing. This image will float above every HTML page served by
-:app:`Pyramid` while you develop an application, and allows you show the
-toolbar as necessary. Click on ``Hide`` to hide the toolbar and show the
-image again.
+.. image:: project-show-toolbar.png
+
+If you click on the :app:`Pyramid` logo at the top right of the page, a new
+target window will open to present a debug toolbar that provides various
+niceties while you're developing. This logo will float above every HTML page
+served by :app:`Pyramid` while you develop an application, and allows you to
+show the toolbar as necessary.
.. image:: project-debug.png
-If you don't see the debug toolbar image on the right hand top of the page,
-it means you're browsing from a system that does not have debugging access.
-By default, for security reasons, only a browser originating from
-``localhost`` (``127.0.0.1``) can see the debug toolbar. To allow your
-browser on a remote system to access the server, add a line within the
-``[app:main]`` section of the ``development.ini`` file in the form
-``debugtoolbar.hosts = X.X.X.X``. For example, if your Pyramid application
-is running on a remote system, and you're browsing from a host with the IP
-address ``192.168.1.1``, you'd add something like this to enable the toolbar
-when your system contacts Pyramid:
+If you don't see the Pyramid logo on the top right of the page, it means you're
+browsing from a system that does not have debugging access. By default, for
+security reasons, only a browser originating from ``localhost`` (``127.0.0.1``)
+can see the debug toolbar. To allow your browser on a remote system to access
+the server, add a line within the ``[app:main]`` section of the
+``development.ini`` file in the form ``debugtoolbar.hosts = X .X.X.X``. For
+example, if your Pyramid application is running on a remote system, and you're
+browsing from a host with the IP address ``192.168.1.1``, you'd add something
+like this to enable the toolbar when your system contacts Pyramid:
.. code-block:: ini
@@ -422,9 +418,9 @@ For more information about what the debug toolbar allows you to do, see `the
documentation for pyramid_debugtoolbar
<http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest/>`_.
-The debug toolbar will not be shown (and all debugging will be turned off)
-when you use the ``production.ini`` file instead of the ``development.ini``
-ini file to run the application.
+The debug toolbar will not be shown (and all debugging will be turned off) when
+you use the ``production.ini`` file instead of the ``development.ini`` ini file
+to run the application.
You can also turn the debug toolbar off by editing ``development.ini`` and
commenting out a line. For example, instead of:
@@ -450,9 +446,9 @@ Put a hash mark at the beginning of the ``pyramid_debugtoolbar`` line:
Then restart the application to see that the toolbar has been turned off.
Note that if you comment out the ``pyramid_debugtoolbar`` line, the ``#``
-*must* be in the first column. If you put it anywhere else,
-and then attempt to restart the application,
-you'll receive an error that ends something like this:
+*must* be in the first column. If you put it anywhere else, and then attempt
+to restart the application, you'll receive an error that ends something like
+this:
.. code-block:: text
@@ -469,9 +465,8 @@ which contains a Python :term:`package`. The package is *also* named
``myproject``, but it's lowercased; the scaffold generates a project which
contains a package that shares its name except for case.
-All :app:`Pyramid` ``pcreate`` -generated projects share a similar structure.
-The ``MyProject`` project we've generated has the following directory
-structure:
+All :app:`Pyramid` ``pcreate``-generated projects share a similar structure.
+The ``MyProject`` project we've generated has the following directory structure:
.. code-block:: text
@@ -497,29 +492,29 @@ structure:
The ``MyProject`` :term:`Project`
---------------------------------
-The ``MyProject`` :term:`project` directory is the distribution and
-deployment wrapper for your application. It contains both the ``myproject``
+The ``MyProject`` :term:`project` directory is the distribution and deployment
+wrapper for your application. It contains both the ``myproject``
:term:`package` representing your application as well as files used to
describe, run, and test your application.
-#. ``CHANGES.txt`` describes the changes you've made to the application. It
- is conventionally written in :term:`ReStructuredText` format.
+#. ``CHANGES.txt`` describes the changes you've made to the application. It is
+ conventionally written in :term:`ReStructuredText` format.
#. ``README.txt`` describes the application in general. It is conventionally
written in :term:`ReStructuredText` format.
-#. ``development.ini`` is a :term:`PasteDeploy` configuration file that can
- be used to execute your application during development.
+#. ``development.ini`` is a :term:`PasteDeploy` configuration file that can be
+ used to execute your application during development.
-#. ``production.ini`` is a :term:`PasteDeploy` configuration file that can
- be used to execute your application in a production configuration.
+#. ``production.ini`` is a :term:`PasteDeploy` configuration file that can be
+ used to execute your application in a production configuration.
#. ``MANIFEST.in`` is a :term:`distutils` "manifest" file, naming which files
should be included in a source distribution of the package when ``python
setup.py sdist`` is run.
-#. ``setup.py`` is the file you'll use to test and distribute your
- application. It is a standard :term:`setuptools` ``setup.py`` file.
+#. ``setup.py`` is the file you'll use to test and distribute your application.
+ It is a standard :term:`setuptools` ``setup.py`` file.
.. index::
single: PasteDeploy
@@ -530,9 +525,9 @@ describe, run, and test your application.
``development.ini``
~~~~~~~~~~~~~~~~~~~
-The ``development.ini`` file is a :term:`PasteDeploy` configuration file.
-Its purpose is to specify an application to run when you invoke ``pserve``,
-as well as the deployment settings provided to that application.
+The ``development.ini`` file is a :term:`PasteDeploy` configuration file. Its
+purpose is to specify an application to run when you invoke ``pserve``, as well
+as the deployment settings provided to that application.
The generated ``development.ini`` file looks like so:
@@ -541,47 +536,47 @@ The generated ``development.ini`` file looks like so:
:linenos:
This file contains several sections including ``[app:main]``,
-``[server:main]`` and several other sections related to logging
-configuration.
+``[server:main]``, and several other sections related to logging configuration.
The ``[app:main]`` section represents configuration for your :app:`Pyramid`
-application. The ``use`` setting is the only setting required to be present
-in the ``[app:main]`` section. Its default value, ``egg:MyProject``,
-indicates that our MyProject project contains the application that should be
-served. Other settings added to this section are passed as keyword arguments
-to the function named ``main`` in our package's ``__init__.py`` module. You
-can provide startup-time configuration parameters to your application by
-adding more settings to this section.
-
-.. note:: See :ref:`pastedeploy_entry_points` for more information about the
+application. The ``use`` setting is the only setting required to be present in
+the ``[app:main]`` section. Its default value, ``egg:MyProject``, indicates
+that our MyProject project contains the application that should be served.
+Other settings added to this section are passed as keyword arguments to the
+function named ``main`` in our package's ``__init__.py`` module. You can
+provide startup-time configuration parameters to your application by adding
+more settings to this section.
+
+.. seealso:: See :ref:`pastedeploy_entry_points` for more information about the
meaning of the ``use = egg:MyProject`` value in this section.
The ``pyramid.reload_templates`` setting in the ``[app:main]`` section is a
-:app:`Pyramid` -specific setting which is passed into the framework. If it
-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.
+:app:`Pyramid`-specific setting which is passed into the framework. If it
+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
production applications, as template rendering is slowed when it is turned
on.
-The ``pyramid.includes`` setting in the ``[app:main]`` section tells Pyramid
-to "include" configuration from another package. In this case, the line
+The ``pyramid.includes`` setting in the ``[app:main]`` section tells Pyramid to
+"include" configuration from another package. In this case, the line
``pyramid.includes = pyramid_debugtoolbar`` tells Pyramid to include
configuration from the ``pyramid_debugtoolbar`` package. This turns on a
-debugging panel in development mode which will be shown on the right hand
-side of the screen. Including the debug toolbar will also make it possible
-to interactively debug exceptions when an error occurs.
+debugging panel in development mode which can be opened by clicking on the
+:app:`Pyramid` logo on the top right of the screen. Including the debug
+toolbar will also make it possible to interactively debug exceptions when an
+error occurs.
-Various other settings may exist in this section having to do with debugging
-or influencing runtime behavior of a :app:`Pyramid` application. See
+Various other settings may exist in this section having to do with debugging or
+influencing runtime behavior of a :app:`Pyramid` application. See
:ref:`environment_chapter` for more information about these settings.
The name ``main`` in ``[app:main]`` signifies that this is the default
application run by ``pserve`` when it is invoked against this configuration
-file. The name ``main`` is a convention used by PasteDeploy signifying that
-it is the default application.
+file. The name ``main`` is a convention used by PasteDeploy signifying that it
+is the default application.
The ``[server:main]`` section of the configuration file configures a WSGI
server which listens on TCP port 6543. It is configured to listen on all
@@ -592,40 +587,38 @@ access to your system can see your Pyramid application.
The sections that live between the markers ``# Begin logging configuration``
and ``# End logging configuration`` represent Python's standard library
-:mod:`logging` module configuration for your application. The sections
-between these two markers are passed to the `logging module's config file
-configuration engine
-<http://docs.python.org/howto/logging.html#configuring-logging>`_ when the
-``pserve`` or ``pshell`` commands are executed. The default
-configuration sends application logging output to the standard error output
-of your terminal. For more information about logging configuration, see
-:ref:`logging_chapter`.
+:mod:`logging` module configuration for your application. The sections between
+these two markers are passed to the `logging module's config file configuration
+engine <http://docs.python.org/howto/logging.html#configuring-logging>`_ when
+the ``pserve`` or ``pshell`` commands are executed. The default configuration
+sends application logging output to the standard error output of your terminal.
+For more information about logging configuration, see :ref:`logging_chapter`.
See the :term:`PasteDeploy` documentation for more information about other
types of things you can put into this ``.ini`` file, such as other
-applications, :term:`middleware` and alternate :term:`WSGI` server
+applications, :term:`middleware`, and alternate :term:`WSGI` server
implementations.
.. index::
single: production.ini
``production.ini``
-~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~
-The ``production.ini`` file is a :term:`PasteDeploy` configuration file with
-a purpose much like that of ``development.ini``. However, it disables the
-debug toolbar, and filters all log messages except those above the WARN
-level. It also turns off template development options such that templates
-are not automatically reloaded when changed, and turns off all debugging
-options. This file is appropriate to use instead of ``development.ini`` when
-you put your application into production.
+The ``production.ini`` file is a :term:`PasteDeploy` configuration file with a
+purpose much like that of ``development.ini``. However, it disables the debug
+toolbar, and filters all log messages except those above the WARN level. It
+also turns off template development options such that templates are not
+automatically reloaded when changed, and turns off all debugging options. This
+file is appropriate to use instead of ``development.ini`` when you put your
+application into production.
It's important to use ``production.ini`` (and *not* ``development.ini``) to
benchmark your application and put it into production. ``development.ini``
configures your system with a debug toolbar that helps development, but the
inclusion of this toolbar slows down page rendering times by over an order of
-magnitude. The debug toolbar is also a potential security risk if you have
-it configured incorrectly.
+magnitude. The debug toolbar is also a potential security risk if you have it
+configured incorrectly.
.. index::
single: MANIFEST.in
@@ -637,42 +630,40 @@ The ``MANIFEST.in`` file is a :term:`distutils` configuration file which
specifies the non-Python files that should be included when a
:term:`distribution` of your Pyramid project is created when you run ``python
setup.py sdist``. Due to the information contained in the default
-``MANIFEST.in``, an sdist of your Pyramid project will include ``.txt``
-files, ``.ini`` files, ``.rst`` files, graphics files, and template files, as
-well as ``.py`` files. See
+``MANIFEST.in``, an sdist of your Pyramid project will include ``.txt`` files,
+``.ini`` files, ``.rst`` files, graphics files, and template files, as well as
+``.py`` files. See
http://docs.python.org/distutils/sourcedist.html#the-manifest-in-template for
more information about the syntax and usage of ``MANIFEST.in``.
-Without the presence of a ``MANIFEST.in`` file or without checking your
-source code into a version control repository, ``setup.py sdist`` places only
-*Python source files* (files ending with a ``.py`` extension) into tarballs
-generated by ``python setup.py sdist``. This means, for example, if your
-project was not checked into a setuptools-compatible source control system,
-and your project directory didn't contain a ``MANIFEST.in`` file that told
-the ``sdist`` machinery to include ``*.pt`` files, the
-``myproject/templates/mytemplate.pt`` file would not be included in the
-generated tarball.
-
-Projects generated by Pyramid scaffolds include a default ``MANIFEST.in``
-file. The ``MANIFEST.in`` file contains declarations which tell it to
-include files like ``*.pt``, ``*.css`` and ``*.js`` in the generated tarball.
-If you include files with extensions other than the files named in the
-project's ``MANIFEST.in`` and you don't make use of a setuptools-compatible
-version control system, you'll need to edit the ``MANIFEST.in`` file and
-include the statements necessary to include your new files. See
-http://docs.python.org/distutils/sourcedist.html#principle for more
-information about how to do this.
-
-You can also delete ``MANIFEST.in`` from your project and rely on a
-setuptools feature which simply causes all files checked into a version
-control system to be put into the generated tarball. To allow this to
-happen, check all the files that you'd like to be distributed along with your
-application's Python files into Subversion. After you do this, when you
-rerun ``setup.py sdist``, all files checked into the version control system
-will be included in the tarball. If you don't use Subversion, and instead
-use a different version control system, you may need to install a setuptools
-add-on such as ``setuptools-git`` or ``setuptools-hg`` for this behavior to
-work properly.
+Without the presence of a ``MANIFEST.in`` file or without checking your source
+code into a version control repository, ``setup.py sdist`` places only *Python
+source files* (files ending with a ``.py`` extension) into tarballs generated
+by ``python setup.py sdist``. This means, for example, if your project was not
+checked into a setuptools-compatible source control system, and your project
+directory didn't contain a ``MANIFEST.in`` file that told the ``sdist``
+machinery to include ``*.pt`` files, the ``myproject/templates/mytemplate.pt``
+file would not be included in the generated tarball.
+
+Projects generated by Pyramid scaffolds include a default ``MANIFEST.in`` file.
+The ``MANIFEST.in`` file contains declarations which tell it to include files
+like ``*.pt``, ``*.css`` and ``*.js`` in the generated tarball. If you include
+files with extensions other than the files named in the project's
+``MANIFEST.in`` and you don't make use of a setuptools-compatible version
+control system, you'll need to edit the ``MANIFEST.in`` file and include the
+statements necessary to include your new files. See
+http://docs.python.org/distutils/sourcedist.html#principle for more information
+about how to do this.
+
+You can also delete ``MANIFEST.in`` from your project and rely on a setuptools
+feature which simply causes all files checked into a version control system to
+be put into the generated tarball. To allow this to happen, check all the
+files that you'd like to be distributed along with your application's Python
+files into Subversion. After you do this, when you rerun ``setup.py sdist``,
+all files checked into the version control system will be included in the
+tarball. If you don't use Subversion, and instead use a different version
+control system, you may need to install a setuptools add-on such as
+``setuptools-git`` or ``setuptools-hg`` for this behavior to work properly.
.. index::
single: setup.py
@@ -686,11 +677,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:
@@ -699,47 +690,47 @@ Our generated ``setup.py`` looks like this:
:linenos:
The ``setup.py`` file calls the setuptools ``setup`` function, which does
-various things depending on the arguments passed to ``setup.py`` on the
-command line.
+various things depending on the arguments passed to ``setup.py`` on the command
+line.
-Within the arguments to this function call, information about your
-application is kept. While it's beyond the scope of this documentation to
-explain everything about setuptools setup files, we'll provide a whirlwind
-tour of what exists in this file in this section.
+Within the arguments to this function call, information about your application
+is kept. While it's beyond the scope of this documentation to explain
+everything about setuptools setup files, we'll provide a whirlwind tour of what
+exists in this file in this section.
Your application's name can be any string; it is specified in the ``name``
field. The version number is specified in the ``version`` value. A short
-description is provided in the ``description`` field. The
-``long_description`` is conventionally the content of the README and CHANGES
-file appended together. The ``classifiers`` field is a list of `Trove
+description is provided in the ``description`` field. The ``long_description``
+is conventionally the content of the README and CHANGES file appended together.
+The ``classifiers`` field is a list of `Trove
<http://pypi.python.org/pypi?%3Aaction=list_classifiers>`_ classifiers
describing your application. ``author`` and ``author_email`` are text fields
which probably don't need any description. ``url`` is a field that should
-point at your application project's URL (if any).
-``packages=find_packages()`` causes all packages within the project to be
-found when packaging the application. ``include_package_data`` will include
-non-Python files when the application is packaged if those files are checked
-into version control. ``zip_safe`` indicates that this package is not safe
-to use as a zipped egg; instead it will always unpack as a directory, which
-is more convenient. ``install_requires`` and ``tests_require`` indicate that
-this package depends on the ``pyramid`` package. ``test_suite`` points at
-the package for our application, which means all tests found in the package
-will be run when ``setup.py test`` is invoked. We examined ``entry_points``
-in our discussion of the ``development.ini`` file; this file defines the
-``main`` entry point that represents our project's application.
-
-Usually you only need to think about the contents of the ``setup.py`` file
-when distributing your application to other people, when adding Python
-package dependencies, or when versioning your application for your own use.
-For fun, you can try this command now:
+point at your application project's URL (if any). ``packages=find_packages()``
+causes all packages within the project to be found when packaging the
+application. ``include_package_data`` will include non-Python files when the
+application is packaged if those files are checked into version control.
+``zip_safe`` indicates that this package is not safe to use as a zipped egg;
+instead it will always unpack as a directory, which is more convenient.
+``install_requires`` and ``tests_require`` indicate that this package depends
+on the ``pyramid`` package. ``test_suite`` points at the package for our
+application, which means all tests found in the package will be run when
+``setup.py test`` is invoked. We examined ``entry_points`` in our discussion
+of the ``development.ini`` file; this file defines the ``main`` entry point
+that represents our project's application.
+
+Usually you only need to think about the contents of the ``setup.py`` file when
+distributing your application to other people, when adding Python package
+dependencies, or when versioning your application for your own use. For fun,
+you can try this command now:
.. code-block:: text
- $ python setup.py sdist
+ $ $VENV/bin/python setup.py sdist
-This will create a tarball of your application in a ``dist`` subdirectory
-named ``MyProject-0.1.tar.gz``. You can send this tarball to other people
-who want to install and use your application.
+This will create a tarball of your application in a ``dist`` subdirectory named
+``MyProject-0.1.tar.gz``. You can send this tarball to other people who want
+to install and use your application.
.. index::
single: package
@@ -750,25 +741,22 @@ The ``myproject`` :term:`Package`
The ``myproject`` :term:`package` lives inside the ``MyProject``
:term:`project`. It contains:
-#. An ``__init__.py`` file signifies that this is a Python :term:`package`.
- It also contains code that helps users run the application, including a
+#. An ``__init__.py`` file signifies that this is a Python :term:`package`. It
+ also contains code that helps users run the application, including a
``main`` function which is used as a entry point for commands such as
``pserve``, ``pshell``, ``pviews``, and others.
-#. A ``templates`` directory, which contains :term:`Chameleon` (or
- other types of) templates.
+#. A ``templates`` directory, which contains :term:`Chameleon` (or other types
+ of) templates.
-#. A ``tests.py`` module, which contains unit test code for the
- application.
+#. A ``tests.py`` module, which contains unit test code for the application.
-#. A ``views.py`` module, which contains view code for the
- application.
+#. A ``views.py`` module, which contains view code for the application.
-These are purely conventions established by the scaffold:
-:app:`Pyramid` doesn't insist that you name things in any particular way.
-However, it's generally a good idea to follow Pyramid standards for naming,
-so that other Pyramid developers can get up to speed quickly on your code
-when you need help.
+These are purely conventions established by the scaffold. :app:`Pyramid`
+doesn't insist that you name things in any particular way. However, it's
+generally a good idea to follow Pyramid standards for naming, so that other
+Pyramid developers can get up to speed quickly on your code when you need help.
.. index::
single: __init__.py
@@ -802,11 +790,11 @@ also informs Python that the directory which contains it is a *package*.
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).
+ ``myproject:static`` :term:`asset specification` (the ``static`` directory
+ of the ``myproject`` package).
- Line 10 adds a :term:`route` to the configuration. This route is later
- used by a view in the ``views`` module.
+ Line 10 adds a :term:`route` to the configuration. This route is later used
+ by a view in the ``views`` module.
Line 11 calls ``config.scan()``, which picks up view registrations declared
elsewhere in the package (in this case, in the ``views.py`` module).
@@ -822,64 +810,63 @@ also informs Python that the directory which contains it is a *package*.
Much of the heavy lifting in a :app:`Pyramid` application is done by *view
callables*. A :term:`view callable` is the main tool of a :app:`Pyramid` web
-application developer; it is a bit of code which accepts a :term:`request`
-and which returns a :term:`response`.
+application developer; it is a bit of code which accepts a :term:`request` and
+which returns a :term:`response`.
.. literalinclude:: MyProject/myproject/views.py
:language: python
:linenos:
Lines 4-6 define and register a :term:`view callable` named ``my_view``. The
-function named ``my_view`` is decorated with a ``view_config`` decorator
-(which is processed by the ``config.scan()`` line in our ``__init__.py``).
-The view_config decorator asserts that this view be found when a
-:term:`route` named ``home`` is matched. In our case, because our
-``__init__.py`` maps the route named ``home`` to the URL pattern ``/``, this
-route will match when a visitor visits the root URL. The view_config
-decorator also names a ``renderer``, which in this case is a template that
-will be used to render the result of the view callable. This particular view
-declaration points at ``templates/mytemplate.pt``, which is a :term:`asset
-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 pointed to is a :term:`Chameleon` ZPT
-template file (``templates/my_template.pt``).
+function named ``my_view`` is decorated with a ``view_config`` decorator (which
+is processed by the ``config.scan()`` line in our ``__init__.py``). The
+view_config decorator asserts that this view be found when a :term:`route`
+named ``home`` is matched. In our case, because our ``__init__.py`` maps the
+route named ``home`` to the URL pattern ``/``, this route will match when a
+visitor visits the root URL. The view_config decorator also names a
+``renderer``, which in this case is a template that will be used to render the
+result of the view callable. This particular view declaration points at
+``templates/mytemplate.pt``, which is an :term:`asset 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 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.
+:term:`request`. The *request* is an instance of the :term:`WebOb` ``Request``
+class representing the browser's request to our server.
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`.
+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:: When the application is run with the scaffold's :ref:`default
- development.ini <MyProject_ini>` configuration :ref:`logging is set up
+ development.ini <MyProject_ini>` configuration, :ref:`logging is set up
<MyProject_ini_logging>` to aid debugging. If an exception is raised,
uncaught tracebacks are displayed after the startup messages on :ref:`the
console running the server <running_the_project_application>`. Also
- ``print()`` statements may be inserted into the application for debugging
- to send output to this console.
+ ``print()`` statements may be inserted into the application for debugging to
+ send output to this console.
.. 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
+ - 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
+ - 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.
+ See also :ref:`views_which_use_a_renderer` for more information about how
+ views, renderers, and templates relate and cooperate.
.. seealso::
@@ -905,10 +892,10 @@ template. It includes CSS and images.
``templates/mytemplate.pt``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The single :term:`Chameleon` template that exists in the project. Its
+This is the single :term:`Chameleon` template that exists in the project. Its
contents are too long to show here, but it displays a default page when
-rendered. It is referenced by the call to ``@view_config`` as the
-``renderer`` of the ``my_view`` view callable in the ``views.py`` file. See
+rendered. It is referenced by the call to ``@view_config`` as the ``renderer``
+of the ``my_view`` view callable in the ``views.py`` file. See
:ref:`views_which_use_a_renderer` for more information about renderers.
Templates are accessed and used by view configurations and sometimes by view
@@ -925,13 +912,13 @@ The ``tests.py`` module includes unit tests for your application.
.. literalinclude:: MyProject/myproject/tests.py
:language: python
+ :lines: 1-18
:linenos:
This sample ``tests.py`` file has a single unit test defined within it. This
-test is executed when you run ``python setup.py test``. You may add more
-tests here as you build your application. You are not required to write
-tests to use :app:`Pyramid`, this file is simply provided as convenience and
-example.
+test is executed when you run ``python setup.py test``. You may add more tests
+here as you build your application. You are not required to write tests to use
+:app:`Pyramid`. This file is simply provided for convenience and example.
See :ref:`testing_chapter` for more information about writing :app:`Pyramid`
unit tests.
@@ -942,30 +929,30 @@ unit tests.
.. _modifying_package_structure:
Modifying Package Structure
-----------------------------
+---------------------------
It is best practice for your application's code layout to not stray too much
-from accepted Pyramid scaffold defaults. If you refrain from changing
-things very much, other Pyramid coders will be able to more quickly
-understand your application. However, the code layout choices made for you
-by a scaffold are in no way magical or required. Despite the choices
-made for you by any scaffold, you can decide to lay your code out any
-way you see fit.
+from accepted Pyramid scaffold defaults. If you refrain from changing things
+very much, other Pyramid coders will be able to more quickly understand your
+application. However, the code layout choices made for you by a scaffold are
+in no way magical or required. Despite the choices made for you by any
+scaffold, you can decide to lay your code out any way you see fit.
For example, the configuration method named
:meth:`~pyramid.config.Configurator.add_view` requires you to pass a
:term:`dotted Python name` or a direct object reference as the class or
-function to be used as a view. By default, the ``starter`` scaffold would
-have you add view functions to the ``views.py`` module in your package.
-However, you might be more comfortable creating a ``views`` *directory*, and
-adding a single file for each view.
+function to be used as a view. By default, the ``starter`` scaffold would have
+you add view functions to the ``views.py`` module in your package. However, you
+might be more comfortable creating a ``views`` *directory*, and adding a single
+file for each view.
If your project package name was ``myproject`` and you wanted to arrange all
your views in a Python subpackage within the ``myproject`` :term:`package`
-named ``views`` instead of within a single ``views.py`` file, you might:
+named ``views`` instead of within a single ``views.py`` file, you might do the
+following.
-- Create a ``views`` directory inside your ``myproject`` package directory
- (the same directory which holds ``views.py``).
+- Create a ``views`` directory inside your ``myproject`` package directory (the
+ same directory which holds ``views.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
@@ -977,72 +964,70 @@ named ``views`` instead of within a single ``views.py`` file, you might:
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
-functions to the ``views`` directory. As long as you use the
-``@view_config`` directive to register views in conjunction with
-``config.scan()`` they will be picked up automatically when the application
-is restarted.
+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 functions
+to the ``views`` directory. As long as you use the ``@view_config`` directive
+to register views in conjunction with ``config.scan()``, they will be picked up
+automatically when the application is restarted.
Using the Interactive Shell
---------------------------
It is possible to use the ``pshell`` command to load a Python interpreter
-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.
+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
-----------------------------
-The code generated by an :app:`Pyramid` scaffold assumes that you will be
-using the ``pserve`` command to start your application while you do
-development. ``pserve`` is a command that reads a :term:`PasteDeploy`
-``.ini`` file (e.g. ``development.ini``) and configures a server to serve a
-Pyramid application based on the data in the file.
+The code generated by a :app:`Pyramid` scaffold assumes that you will be using
+the ``pserve`` command to start your application while you do development.
+``pserve`` is a command that reads a :term:`PasteDeploy` ``.ini`` file (e.g.,
+``development.ini``), and configures a server to serve a :app:`Pyramid`
+application based on the data in the file.
``pserve`` is by no means the only way to start up and serve a :app:`Pyramid`
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 ``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.
+run a :app:`Pyramid` application is purely conventional based on the output 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:
Using an Alternate WSGI Server
------------------------------
-Pyramid scaffolds generate projects which use the :term:`Waitress` WSGI
-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.
+Pyramid scaffolds generate projects which use the :term:`Waitress` WSGI 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.
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
-to investigate other server options until you're ready to deploy your
-application to production. Unless for some reason you need to develop on a
-non-local system, investigating alternate server options is usually a
-distraction until you're ready to deploy. But we recommend developing using
-the default configuration on a local system that you have complete control
-over; it will provide the best development experience.
+suggest you stick with the default server for development, and that you wait to
+investigate other server options until you're ready to deploy your application
+to production. Unless for some reason you need to develop on a non-local
+system, investigating alternate server options is usually a distraction until
+you're ready to deploy. But we recommend developing using the default
+configuration on a local system that you have complete control over; it will
+provide the best development experience.
One popular production alternative to the default Waitress server is
-:term:`mod_wsgi`. You can use mod_wsgi to serve your :app:`Pyramid`
-application using the Apache web server rather than any "pure-Python" server
-like Waitress. It is fast and featureful. See :ref:`modwsgi_tutorial` for
-details.
+:term:`mod_wsgi`. You can use mod_wsgi to serve your :app:`Pyramid` application
+using the Apache web server rather than any "pure-Python" server like Waitress.
+It is fast and featureful. See :ref:`modwsgi_tutorial` for details.
Another good production alternative is :term:`Green Unicorn` (aka
-``gunicorn``). It's faster than Waitress and slightly easier to configure
-than mod_wsgi, although it depends, in its default configuration, on having a
-buffering HTTP proxy in front of it. It does not, as of this writing, work
-on Windows.
+``gunicorn``). It's faster than Waitress and slightly easier to configure than
+mod_wsgi, although it depends, in its default configuration, on having a
+buffering HTTP proxy in front of it. It does not, as of this writing, work on
+Windows.
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 4f8c4bf77..cc5baf05e 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -5,8 +5,8 @@ Renderers
A view callable needn't *always* return a :term:`Response` object. If a view
happens to return something which does not implement the Pyramid Response
-interface, :app:`Pyramid` will attempt to use a :term:`renderer` to construct
-a response. For example:
+interface, :app:`Pyramid` will attempt to use a :term:`renderer` to construct a
+response. For example:
.. code-block:: python
:linenos:
@@ -17,27 +17,26 @@ a response. For example:
def hello_world(request):
return {'content':'Hello!'}
-The above example returns a *dictionary* from the view callable. A
-dictionary does not implement the Pyramid response interface, so you might
-believe that this example would fail. However, since a ``renderer`` is
-associated with the view callable through its :term:`view configuration` (in
-this case, using a ``renderer`` argument passed to
-:func:`~pyramid.view.view_config`), if the view does *not* return a Response
-object, the renderer will attempt to convert the result of the view to a
-response on the developer's behalf.
+The above example returns a *dictionary* from the view callable. A dictionary
+does not implement the Pyramid response interface, so you might believe that
+this example would fail. However, since a ``renderer`` is associated with the
+view callable through its :term:`view configuration` (in this case, using a
+``renderer`` argument passed to :func:`~pyramid.view.view_config`), if the view
+does *not* return a Response object, the renderer will attempt to convert the
+result of the view to a response on the developer's behalf.
-Of course, if no renderer is associated with a view's configuration,
-returning anything except an object which implements the Response interface
-will result in an error. And, if a renderer *is* used, whatever is returned
-by the view must be compatible with the particular kind of renderer used, or
-an error may occur during view invocation.
+Of course, if no renderer is associated with a view's configuration, returning
+anything except an object which implements the Response interface will result
+in an error. And, if a renderer *is* used, whatever is returned by the view
+must be compatible with the particular kind of renderer used, or an error may
+occur during view invocation.
-One exception exists: it is *always* OK to return a Response object, even
-when a ``renderer`` is configured. In such cases, the renderer is
-bypassed entirely.
+One exception exists: it is *always* OK to return a Response object, even 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.
+Various types of renderers exist, including serialization renderers and
+renderers which use templating systems.
.. index::
single: renderer
@@ -49,19 +48,19 @@ Writing View Callables Which Use a Renderer
-------------------------------------------
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
+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, while 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:
+:meth:`~pyramid.config.Configurator.add_view` associates the ``json`` renderer
+with a view callable:
.. code-block:: python
@@ -71,19 +70,19 @@ 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.
-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`.
+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
response attributes (such as headers and the HTTP status code) by attaching a
-property to the ``request.response`` attribute.
-See :ref:`request_response_attr`.
+property to the ``request.response`` attribute. See
+:ref:`request_response_attr`.
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:
+: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:
@@ -126,7 +125,7 @@ avoid rendering:
.. _built_in_renderers:
-Built-In Renderers
+Built-in Renderers
------------------
Several built-in renderers exist in :app:`Pyramid`. These renderers can be
@@ -134,8 +133,8 @@ used in the ``renderer`` attribute of view configurations.
.. note::
- Bindings for officially supported templating languages can be found
- at :ref:`available_template_system_bindings`.
+ Bindings for officially supported templating languages can be found at
+ :ref:`available_template_system_bindings`.
.. index::
pair: renderer; string
@@ -143,17 +142,15 @@ used in the ``renderer`` attribute of view configurations.
``string``: String Renderer
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-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
-string. Note that if a Unicode object is returned by the view callable, it
-is not ``str()`` -ified.
+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 string. Note that if a
+Unicode object is returned by the view callable, it is not ``str()``-ified.
Here's an example of a view that returns a dictionary. If the ``string``
-renderer is specified in the configuration for this view, the view will
-render the returned dictionary to the ``str()`` representation of the
-dictionary:
+renderer is specified in the configuration for this view, the view will render
+the returned dictionary to the ``str()`` representation of the dictionary:
.. code-block:: python
:linenos:
@@ -164,8 +161,8 @@ dictionary:
def hello_world(request):
return {'content':'Hello!'}
-The body of the response returned by such a view will be a string
-representing the ``str()`` serialization of the return value:
+The body of the response returned by such a view will be a string representing
+the ``str()`` serialization of the return value:
.. code-block:: python
@@ -184,13 +181,13 @@ JSON Renderer
~~~~~~~~~~~~~
The ``json`` renderer renders view callable results to :term:`JSON`. By
-default, it passes the return value through the ``json.dumps`` standard
-library function, and wraps the result in a response object. It also sets
-the response content-type to ``application/json``.
+default, it passes the return value through the ``json.dumps`` standard library
+function, and wraps the result in a response object. It also sets the response
+content-type to ``application/json``.
Here's an example of a view that returns a dictionary. Since the ``json``
-renderer is specified in the configuration for this view, the view will
-render the returned dictionary to a JSON serialization:
+renderer is specified in the configuration for this view, the view will render
+the returned dictionary to a JSON serialization:
.. code-block:: python
:linenos:
@@ -201,8 +198,8 @@ render the returned dictionary to a JSON serialization:
def hello_world(request):
return {'content':'Hello!'}
-The body of the response returned by such a view will be a string
-representing the JSON serialization of the return value:
+The body of the response returned by such a view will be a string representing
+the JSON serialization of the return value:
.. code-block:: python
@@ -211,8 +208,8 @@ representing the JSON serialization of the return value:
The return value needn't be a dictionary, but the return value must contain
values serializable by the configured serializer (by default ``json.dumps``).
-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
+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`:
.. code-block:: python
@@ -224,7 +221,7 @@ You can configure a view to use the JSON renderer by naming``json`` as the
renderer='json')
Views which use the JSON renderer can vary non-body response attributes by
-using the api of the ``request.response`` attribute. See
+using the API of the ``request.response`` attribute. See
:ref:`request_response_attr`.
.. _json_serializing_custom_objects:
@@ -232,23 +229,23 @@ 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
+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.
+- Define 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.
+- For objects you don't "own", you can register a 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
-so forth). It should accept a single additional argument, ``request``, which
-will be the active request object at render time.
+natively JSON-serializable (such as ints, lists, dictionaries, strings, and so
+forth). It should accept a single additional argument, ``request``, which will
+be the active request object at render time.
.. code-block:: python
:linenos:
@@ -272,14 +269,14 @@ will be the active request object at render time.
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
-to the renderer is not a serializable type, and has no ``__json__`` method,
-usually a :exc:`TypeError` will be raised during serialization. You can
-change this behavior by creating a custom JSON renderer and adding adapters
-to handle custom types. The renderer will attempt to adapt non-serializable
-objects using the registered adapters. A short example follows:
+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 to the
+renderer is not a serializable type and has no ``__json__`` method, usually a
+:exc:`TypeError` will be raised during serialization. You can change this
+behavior by creating a custom JSON renderer and adding adapters to handle
+custom types. The renderer will attempt to adapt non-serializable objects using
+the registered adapters. A short example follows:
.. code-block:: python
:linenos:
@@ -294,16 +291,17 @@ objects using the registered adapters. A short example follows:
json_renderer.add_adapter(datetime.datetime, datetime_adapter)
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,
+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 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.
+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.
+See :class:`pyramid.renderers.JSON` and :ref:`adding_and_overriding_renderers`
+for more information.
.. versionadded:: 1.4
Serializing custom objects.
@@ -319,12 +317,12 @@ JSONP Renderer
.. versionadded:: 1.1
:class:`pyramid.renderers.JSONP` is a `JSONP
-<http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper which
-implements a hybrid json/jsonp renderer. JSONP is useful for making
-cross-domain AJAX requests.
+<http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper which implements
+a hybrid JSON/JSONP renderer. JSONP is useful for making cross-domain AJAX
+requests.
-Unlike other renderers, a JSONP renderer needs to be configured at startup
-time "by hand". Configure a JSONP renderer using the
+Unlike other renderers, a JSONP renderer needs to be configured at startup time
+"by hand". Configure a JSONP renderer using the
:meth:`pyramid.config.Configurator.add_renderer` method:
.. code-block:: python
@@ -355,8 +353,8 @@ When a view is called that uses a JSONP renderer:
renderer (by default, ``callback``), the renderer will return a JSONP
response.
-- If there is no callback parameter in the request's query string, the
- renderer will return a 'plain' JSON response.
+- If there is no callback parameter in the request's query string, the renderer
+ will return a "plain" JSON response.
Javscript library AJAX functionality will help you make JSONP requests.
For example, JQuery has a `getJSON function
@@ -364,7 +362,7 @@ For example, JQuery has a `getJSON function
complicated) functionality in its `ajax function
<http://api.jquery.com/jQuery.ajax/>`_.
-For example (Javascript):
+For example (JavaScript):
.. code-block:: javascript
@@ -375,10 +373,9 @@ For example (Javascript):
'&callback=?';
jqhxr = $.getJSON(api_url);
-The string ``callback=?`` above in the ``url`` param to the JQuery
-``getJSON`` function indicates to jQuery that the query should be made as
-a JSONP request; the ``callback`` parameter will be automatically filled
-in for you and used.
+The string ``callback=?`` above in the ``url`` param to the JQuery ``getJSON``
+function indicates to jQuery that the query should be made as a JSONP request;
+the ``callback`` parameter will be automatically filled in for you and used.
The same custom-object serialization scheme defined used for a "normal" JSON
renderer in :ref:`json_serializing_custom_objects` can be used when passing
@@ -397,10 +394,9 @@ Before a response constructed by a :term:`renderer` is returned to
:app:`Pyramid`, several attributes of the request are examined which have the
potential to influence response behavior.
-View callables that don't directly return a response should use the API of
-the :class:`pyramid.response.Response` attribute available as
-``request.response`` during their execution, to influence associated response
-behavior.
+View callables that don't directly return a response should use the API of the
+:class:`pyramid.response.Response` attribute, available as ``request.response``
+during their execution, to influence associated response behavior.
For example, if you need to change the response status from within a view
callable that uses a renderer, assign the ``status`` attribute to the
@@ -419,7 +415,7 @@ callable that uses a renderer, assign the ``status`` attribute to the
Note that mutations of ``request.response`` in views which return a Response
object directly will have no effect unless the response object returned *is*
``request.response``. For example, the following example calls
-``request.response.set_cookie``, but this call will have no effect, because a
+``request.response.set_cookie``, but this call will have no effect because a
different Response object is returned.
.. code-block:: python
@@ -441,8 +437,8 @@ effect, you must return ``request.response``:
request.response.set_cookie('abc', '123')
return request.response
-For more information on attributes of the request, see the API documentation
-in :ref:`request_module`. For more information on the API of
+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`.
.. _adding_and_overriding_renderers:
@@ -481,10 +477,9 @@ You may add a new renderer by creating and registering a :term:`renderer
factory`.
A renderer factory implementation should conform to the
-:class:`pyramid.interfaces.IRendererFactory` interface. It should be capable
-of creating an object that conforms to the
-:class:`pyramid.interfaces.IRenderer` interface. A typical class that follows
-this setup is as follows:
+:class:`pyramid.interfaces.IRendererFactory` interface. It should be capable of
+creating an object that conforms to the :class:`pyramid.interfaces.IRenderer`
+interface. A typical class that follows this setup is as follows:
.. code-block:: python
:linenos:
@@ -504,38 +499,36 @@ this setup is as follows:
the result (a string or unicode object). The value is
the return value of a view. The system value is a
dictionary containing available system values
- (e.g. view, context, and request). """
+ (e.g., view, context, and request). """
The formal interface definition of the ``info`` object passed to a renderer
factory constructor is available as :class:`pyramid.interfaces.IRendererInfo`.
There are essentially two different kinds of renderer factories:
-- A renderer factory which expects to accept an :term:`asset
- specification`, or an absolute path, as the ``name`` attribute of the
- ``info`` object fed to its constructor. These renderer factories are
- registered with a ``name`` value that begins with a dot (``.``). These
- types of renderer factories usually relate to a file on the filesystem,
- such as a template.
+- A renderer factory which expects to accept an :term:`asset specification`, or
+ an absolute path, as the ``name`` attribute of the ``info`` object fed to its
+ constructor. These renderer factories are registered with a ``name`` value
+ that begins with a dot (``.``). These types of renderer factories usually
+ relate to a file on the filesystem, such as a template.
-- A renderer factory which expects to accept a token that does not represent
- a filesystem path or an asset specification in the ``name``
- attribute of the ``info`` object fed to its constructor. These renderer
- factories are registered with a ``name`` value that does not begin with a
- dot. These renderer factories are typically object serializers.
+- A renderer factory which expects to accept a token that does not represent a
+ filesystem path or an asset specification in the ``name`` attribute of the
+ ``info`` object fed to its constructor. These renderer factories are
+ registered with a ``name`` value that does not begin with a dot. These
+ renderer factories are typically object serializers.
.. sidebar:: Asset Specifications
- An asset specification is a colon-delimited identifier for an
- :term:`asset`. The colon separates a Python :term:`package`
- name from a package subpath. For example, the asset
- specification ``my.package:static/baz.css`` identifies the file named
- ``baz.css`` in the ``static`` subdirectory of the ``my.package`` Python
- :term:`package`.
+ An asset specification is a colon-delimited identifier for an :term:`asset`.
+ The colon separates a Python :term:`package` name from a package subpath.
+ For example, the asset specification ``my.package:static/baz.css``
+ identifies the file named ``baz.css`` in the ``static`` subdirectory of the
+ ``my.package`` Python :term:`package`.
Here's an example of the registration of a simple renderer factory via
-:meth:`~pyramid.config.Configurator.add_renderer`, where ``config``
-is an instance of :meth:`pyramid.config.Configurator`:
+:meth:`~pyramid.config.Configurator.add_renderer`, where ``config`` is an
+instance of :meth:`pyramid.config.Configurator`:
.. code-block:: python
@@ -556,16 +549,15 @@ renderer by specifying ``amf`` in the ``renderer`` attribute of a
def myview(request):
return {'Hello':'world'}
-At startup time, when a :term:`view configuration` is encountered, which
-has a ``name`` attribute that does not contain a dot, the full ``name``
-value is used to construct a renderer from the associated renderer
-factory. In this case, the view configuration will create an instance
-of an ``MyAMFRenderer`` for each view configuration which includes ``amf``
-as its renderer value. The ``name`` passed to the ``MyAMFRenderer``
-constructor will always be ``amf``.
+At startup time, when a :term:`view configuration` is encountered which has a
+``name`` attribute that does not contain a dot, the full ``name`` value is used
+to construct a renderer from the associated renderer factory. In this case,
+the view configuration will create an instance of an ``MyAMFRenderer`` for each
+view configuration which includes ``amf`` as its renderer value. The ``name``
+passed to the ``MyAMFRenderer`` constructor will always be ``amf``.
-Here's an example of the registration of a more complicated renderer
-factory, which expects to be passed a filesystem path:
+Here's an example of the registration of a more complicated renderer factory,
+which expects to be passed a filesystem path:
.. code-block:: python
@@ -585,24 +577,23 @@ the ``renderer`` attribute of a :term:`view configuration`:
def myview(request):
return {'Hello':'world'}
-When a :term:`view configuration` is encountered at startup time, which
-has a ``name`` attribute that does contain a dot, the value of the name
-attribute is split on its final dot. The second element of the split is
-typically the filename extension. This extension is used to look up a
-renderer factory for the configured view. Then the value of
-``renderer`` is passed to the factory to create a renderer for the view.
-In this case, the view configuration will create an instance of a
-``MyJinja2Renderer`` for each view configuration which includes anything
-ending with ``.jinja2`` in its ``renderer`` value. The ``name`` passed
-to the ``MyJinja2Renderer`` constructor will be the full value that was
-set as ``renderer=`` in the view configuration.
+When a :term:`view configuration` is encountered at startup time which has a
+``name`` attribute that does contain a dot, the value of the name attribute is
+split on its final dot. The second element of the split is typically the
+filename extension. This extension is used to look up a renderer factory for
+the configured view. Then the value of ``renderer`` is passed to the factory
+to create a renderer for the view. In this case, the view configuration will
+create an instance of a ``MyJinja2Renderer`` for each view configuration which
+includes anything ending with ``.jinja2`` in its ``renderer`` value. The
+``name`` passed to the ``MyJinja2Renderer`` constructor will be the full value
+that was set as ``renderer=`` in the view configuration.
Adding a Default 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:
+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
@@ -616,40 +607,40 @@ Changing an Existing Renderer
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
+means that, in most cases, overriding a renderer is as simple as using the
+:meth:`pyramid.config.Configurator.add_renderer` method to redefine the
template extension. For example, if you would like to override the ``.txt``
-extension to specify a new renderer you could do the following:
+extension to specify a new renderer, you could do the following:
.. code-block:: python
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.
+After doing this, any views registered with the ``json`` renderer will use the
+new renderer.
.. index::
pair: renderer; overriding at runtime
-Overriding A Renderer At Runtime
+Overriding a Renderer at Runtime
--------------------------------
.. warning:: This is an advanced feature, not typically used by "civilians".
In some circumstances, it is necessary to instruct the system to ignore the
static renderer declaration provided by the developer in view configuration,
-replacing the renderer with another *after a request starts*. For example,
-an "omnipresent" XML-RPC implementation that detects that the request is from
-an XML-RPC client might override a view configuration statement made by the
-user instructing the view to use a template renderer with one that uses an
-XML-RPC renderer. This renderer would produce an XML-RPC representation of
-the data returned by an arbitrary view callable.
+replacing the renderer with another *after a request starts*. For example, an
+"omnipresent" XML-RPC implementation that detects that the request is from an
+XML-RPC client might override a view configuration statement made by the user
+instructing the view to use a template renderer with one that uses an XML-RPC
+renderer. This renderer would produce an XML-RPC representation of the data
+returned by an arbitrary view callable.
To use this feature, create a :class:`~pyramid.events.NewRequest`
:term:`subscriber` which sniffs at the request data and which conditionally
-sets an ``override_renderer`` attribute on the request itself, which is the
-*name* of a registered renderer. For example:
+sets an ``override_renderer`` attribute on the request itself, which in turn is
+the *name* of a registered renderer. For example:
.. code-block:: python
:linenos:
@@ -670,6 +661,6 @@ sets an ``override_renderer`` attribute on the request itself, which is the
request.override_renderer = 'xmlrpc'
return True
-The result of such a subscriber will be to replace any existing static
-renderer configured by the developer with a (notional, nonexistent) XML-RPC
-renderer if the request appears to come from an XML-RPC client.
+The result of such a subscriber will be to replace any existing static renderer
+configured by the developer with a (notional, nonexistent) XML-RPC renderer, if
+the request appears to come from an XML-RPC client.
diff --git a/docs/narr/router.rst b/docs/narr/router.rst
index 6f90c70cc..e02142e6e 100644
--- a/docs/narr/router.rst
+++ b/docs/narr/router.rst
@@ -17,12 +17,12 @@ requests and return responses. What happens from the time a :term:`WSGI`
request enters a :app:`Pyramid` application through to the point that
:app:`Pyramid` hands off a response back to WSGI for upstream processing?
-#. A user initiates a request from his browser to the hostname and port
+#. A user initiates a request from their browser to the hostname and port
number of the WSGI server used by the :app:`Pyramid` application.
#. The WSGI server used by the :app:`Pyramid` application passes the WSGI
- environment to the ``__call__`` method of the :app:`Pyramid`
- :term:`router` object.
+ environment to the ``__call__`` method of the :app:`Pyramid` :term:`router`
+ object.
#. A :term:`request` object is created based on the WSGI environment.
@@ -35,41 +35,40 @@ request enters a :app:`Pyramid` application through to the point that
#. A :class:`~pyramid.events.NewRequest` :term:`event` is sent to any
subscribers.
-#. If any :term:`route` has been defined within application configuration,
- the :app:`Pyramid` :term:`router` calls a :term:`URL dispatch` "route
- mapper." The job of the mapper is to examine the request to determine
- whether any user-defined :term:`route` matches the current WSGI
- environment. The :term:`router` passes the request as an argument to the
- mapper.
+#. If any :term:`route` has been defined within application configuration, the
+ :app:`Pyramid` :term:`router` calls a :term:`URL dispatch` "route mapper."
+ The job of the mapper is to examine the request to determine whether any
+ user-defined :term:`route` matches the current WSGI environment. The
+ :term:`router` passes the request as an argument to the mapper.
#. If any route matches, the route mapper adds attributes to the request:
``matchdict`` and ``matched_route`` attributes are added to the request
object. The former contains a dictionary representing the matched dynamic
- elements of the request's ``PATH_INFO`` value, the latter contains the
+ elements of the request's ``PATH_INFO`` value, and the latter contains the
:class:`~pyramid.interfaces.IRoute` object representing the route which
- matched. The root object associated with the route found is also
- generated: if the :term:`route configuration` which matched has an
- associated ``factory`` argument, this factory is used to generate the
- root object, otherwise a default :term:`root factory` is used.
-
-#. If a route match was *not* found, and a ``root_factory`` argument was
- passed to the :term:`Configurator` constructor, that callable is used to
- generate the root object. If the ``root_factory`` argument passed to the
+ matched. The root object associated with the route found is also generated:
+ if the :term:`route configuration` which matched has an associated
+ ``factory`` argument, this factory is used to generate the root object,
+ otherwise a default :term:`root factory` is used.
+
+#. If a route match was *not* found, and a ``root_factory`` argument was passed
+ to the :term:`Configurator` constructor, that callable is used to generate
+ the root object. If the ``root_factory`` argument passed to the
Configurator constructor was ``None``, a default root factory is used to
generate a root object.
-#. The :app:`Pyramid` router calls a "traverser" function with the root
- object and the request. The traverser function attempts to traverse the
- root object (using any existing ``__getitem__`` on the root object and
+#. The :app:`Pyramid` router calls a "traverser" function with the root object
+ and the request. The traverser function attempts to traverse the root
+ object (using any existing ``__getitem__`` on the root object and
subobjects) to find a :term:`context`. If the root object has no
- ``__getitem__`` method, the root itself is assumed to be the context. The
+ ``__getitem__`` method, the root itself is assumed to be the context. The
exact traversal algorithm is described in :ref:`traversal_chapter`. The
traverser function returns a dictionary, which contains a :term:`context`
and a :term:`view name` as well as other ancillary information.
#. The request is decorated with various names returned from the traverser
- (such as ``context``, ``view_name``, and so forth), so they can be
- accessed via e.g. ``request.context`` within :term:`view` code.
+ (such as ``context``, ``view_name``, and so forth), so they can be accessed
+ via, for example, ``request.context`` within :term:`view` code.
#. A :class:`~pyramid.events.ContextFound` :term:`event` is sent to any
subscribers.
@@ -87,34 +86,33 @@ request enters a :app:`Pyramid` application through to the point that
protected by a :term:`permission`, :app:`Pyramid` determines whether the
view callable being asked for can be executed by the requesting user based
on credential information in the request and security information attached
- to the context. If the view execution is allowed, :app:`Pyramid` calls
- the view callable to obtain a response. If view execution is forbidden,
+ to the context. If the view execution is allowed, :app:`Pyramid` calls the
+ view callable to obtain a response. If view execution is forbidden,
:app:`Pyramid` raises a :class:`~pyramid.httpexceptions.HTTPForbidden`
exception.
#. If any exception is raised within a :term:`root factory`, by
- :term:`traversal`, by a :term:`view callable` or by :app:`Pyramid` itself
+ :term:`traversal`, by a :term:`view callable`, or by :app:`Pyramid` itself
(such as when it raises :class:`~pyramid.httpexceptions.HTTPNotFound` or
:class:`~pyramid.httpexceptions.HTTPForbidden`), the router catches the
exception, and attaches it to the request as the ``exception`` attribute.
- It then attempts to find a :term:`exception view` for the exception that
- was caught. If it finds an exception view callable, that callable is
- called, and is presumed to generate a response. If an :term:`exception
- view` that matches the exception cannot be found, the exception is
- reraised.
-
-#. The following steps occur only when a :term:`response` could be
- successfully generated by a normal :term:`view callable` or an
- :term:`exception view` callable. :app:`Pyramid` will attempt to execute
- any :term:`response callback` functions attached via
- :meth:`~pyramid.request.Request.add_response_callback`. A
+ It then attempts to find a :term:`exception view` for the exception that was
+ caught. If it finds an exception view callable, that callable is called,
+ and is presumed to generate a response. If an :term:`exception view` that
+ matches the exception cannot be found, the exception is reraised.
+
+#. The following steps occur only when a :term:`response` could be successfully
+ generated by a normal :term:`view callable` or an :term:`exception view`
+ callable. :app:`Pyramid` will attempt to execute any :term:`response
+ callback` functions attached via
+ :meth:`~pyramid.request.Request.add_response_callback`. A
:class:`~pyramid.events.NewResponse` :term:`event` is then sent to any
subscribers. The response object's ``__call__`` method is then used to
generate a WSGI response. The response is sent back to the upstream WSGI
server.
-#. :app:`Pyramid` will attempt to execute any :term:`finished
- callback` functions attached via
+#. :app:`Pyramid` will attempt to execute any :term:`finished callback`
+ functions attached via
:meth:`~pyramid.request.Request.add_finished_callback`.
#. The :term:`thread local` stack is popped.
@@ -123,7 +121,6 @@ request enters a :app:`Pyramid` application through to the point that
:alt: Pyramid Router
This is a very high-level overview that leaves out various details. For more
-detail about subsystems invoked by the :app:`Pyramid` router such as
+detail about subsystems invoked by the :app:`Pyramid` router, such as
traversal, URL dispatch, views, and event processing, see
:ref:`urldispatch_chapter`, :ref:`views_chapter`, and :ref:`events_chapter`.
-
diff --git a/docs/narr/scaffolding.rst b/docs/narr/scaffolding.rst
index f924d0d62..4fcdeb537 100644
--- a/docs/narr/scaffolding.rst
+++ b/docs/narr/scaffolding.rst
@@ -57,6 +57,11 @@ As you create files and directories within the template directory, note that:
have that string replaced with the value of the ``var`` variable provided
to the scaffold.
+- Files that start with a dot (e.g., ``.env``) are ignored and will not be
+ copied over to the destination directory. If you want to include a file with
+ a leading dot then you must replace the dot with ``+dot+`` (e.g.,
+ ``+dot+env``).
+
Otherwise, files and directories which live in the template directory will be
copied directly without modification to the ``pcreate`` output location.
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index 5c103405a..db554a93b 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -6,45 +6,44 @@
Sessions
========
-A :term:`session` is a namespace which is valid for some period of
-continual activity that can be used to represent a user's interaction
-with a web application.
+A :term:`session` is a namespace which is valid for some period of continual
+activity that can be used to represent a user's interaction with a web
+application.
-This chapter describes how to configure sessions, what session
-implementations :app:`Pyramid` provides out of the box, how to store and
-retrieve data from sessions, and two session-specific features: flash
-messages, and cross-site request forgery attack prevention.
+This chapter describes how to configure sessions, what session implementations
+:app:`Pyramid` provides out of the box, how to store and retrieve data from
+sessions, and two session-specific features: flash messages, and cross-site
+request forgery attack prevention.
.. index::
single: session factory (default)
.. _using_the_default_session_factory:
-Using The Default Session Factory
+Using the Default Session Factory
---------------------------------
-In order to use sessions, you must set up a :term:`session factory`
-during your :app:`Pyramid` configuration.
+In order to use sessions, you must set up a :term:`session factory` during your
+:app:`Pyramid` configuration.
-A very basic, insecure sample session factory implementation is
-provided in the :app:`Pyramid` core. It uses a cookie to store
-session information. This implementation has the following
-limitations:
+A very basic, insecure sample session factory implementation is provided in the
+:app:`Pyramid` core. It uses a cookie to store session information. This
+implementation has the following limitations:
-- The session information in the cookies used by this implementation
- is *not* encrypted, so it can be viewed by anyone with access to the
- cookie storage of the user's browser or anyone with access to the
- network along which the cookie travels.
+- The session information in the cookies used by this implementation is *not*
+ encrypted, so it can be viewed by anyone with access to the cookie storage of
+ the user's browser or anyone with access to the network along which the
+ cookie travels.
-- The maximum number of bytes that are storable in a serialized
- representation of the session is fewer than 4000. This is
- suitable only for very small data sets.
+- The maximum number of bytes that are storable in a serialized representation
+ of the session is fewer than 4000. This is suitable only for very small data
+ sets.
-It is digitally signed, however, and thus its data cannot easily be
-tampered with.
+It is digitally signed, however, and thus its data cannot easily be tampered
+with.
-You can configure this session factory in your :app:`Pyramid` application
-by using the :meth:`pyramid.config.Configurator.set_session_factory` method.
+You can configure this session factory in your :app:`Pyramid` application by
+using the :meth:`pyramid.config.Configurator.set_session_factory` method.
.. code-block:: python
:linenos:
@@ -56,20 +55,20 @@ by using the :meth:`pyramid.config.Configurator.set_session_factory` method.
config = Configurator()
config.set_session_factory(my_session_factory)
-.. warning::
+.. warning::
By default the :func:`~pyramid.session.SignedCookieSessionFactory`
- implementation is *unencrypted*. You should not use it
- when you keep sensitive information in the session object, as the
- information can be easily read by both users of your application and third
- parties who have access to your users' network traffic. And if you use this
- sessioning implementation, and you inadvertently create a cross-site
- scripting vulnerability in your application, because the session data is
- stored unencrypted in a cookie, it will also be easier for evildoers to
- obtain the current user's cross-site scripting token. In short, use a
- different session factory implementation (preferably one which keeps session
- data on the server) for anything but the most basic of applications where
- "session security doesn't matter", and you are sure your application has no
+ implementation is *unencrypted*. You should not use it when you keep
+ sensitive information in the session object, as the information can be
+ easily read by both users of your application and third parties who have
+ access to your users' network traffic. And, if you use this sessioning
+ implementation, and you inadvertently create a cross-site scripting
+ vulnerability in your application, because the session data is stored
+ unencrypted in a cookie, it will also be easier for evildoers to obtain the
+ current user's cross-site scripting token. In short, use a different
+ session factory implementation (preferably one which keeps session data on
+ the server) for anything but the most basic of applications where "session
+ security doesn't matter", and you are sure your application has no
cross-site scripting vulnerabilities.
.. index::
@@ -78,10 +77,9 @@ by using the :meth:`pyramid.config.Configurator.set_session_factory` method.
Using a Session Object
----------------------
-Once a session factory has been configured for your application, you
-can access session objects provided by the session factory via
-the ``session`` attribute of any :term:`request` object. For
-example:
+Once a session factory has been configured for your application, you can access
+session objects provided by the session factory via the ``session`` attribute
+of any :term:`request` object. For example:
.. code-block:: python
:linenos:
@@ -98,13 +96,12 @@ 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.
+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.
+dictionary methods, along with some extra attributes and methods.
Extra attributes:
@@ -112,42 +109,39 @@ Extra attributes:
An integer timestamp indicating the time that this session was created.
``new``
- A boolean. If ``new`` is True, this session is new. Otherwise, it has
- been constituted from data that was already serialized.
+ A boolean. If ``new`` is True, this session is new. Otherwise, it has been
+ constituted from data that was already serialized.
Extra methods:
``changed()``
- Call this when you mutate a mutable value in the session namespace.
- See the gotchas below for details on when, and why you should
- call this.
+ Call this when you mutate a mutable value in the session namespace. See the
+ gotchas below for details on when and why you should call this.
``invalidate()``
- Call this when you want to invalidate the session (dump all data,
- and -- perhaps -- set a clearing cookie).
+ Call this when you want to invalidate the session (dump all data, and perhaps
+ set a clearing cookie).
-The formal definition of the methods and attributes supported by the
-session object are in the :class:`pyramid.interfaces.ISession`
-documentation.
+The formal definition of the methods and attributes supported by the session
+object are in the :class:`pyramid.interfaces.ISession` documentation.
Some gotchas:
-- Keys and values of session data must be *pickleable*. This means,
- typically, that they are instances of basic types of objects,
- such as strings, lists, dictionaries, tuples, integers, etc. If you
- place an object in a session data key or value that is not
- pickleable, an error will be raised when the session is serialized.
-
-- If you place a mutable value (for example, a list or a dictionary)
- in a session object, and you subsequently mutate that value, you must
- call the ``changed()`` method of the session object. In this case, the
- session has no way to know that is was modified. However, when you
- modify a session object directly, such as setting a value (i.e.,
- ``__setitem__``), or removing a key (e.g., ``del`` or ``pop``), the
- session will automatically know that it needs to re-serialize its
- data, thus calling ``changed()`` is unnecessary. There is no harm in
- calling ``changed()`` in either case, so when in doubt, call it after
- you've changed sessioning data.
+- Keys and values of session data must be *pickleable*. This means, typically,
+ that they are instances of basic types of objects, such as strings, lists,
+ dictionaries, tuples, integers, etc. If you place an object in a session
+ data key or value that is not pickleable, an error will be raised when the
+ session is serialized.
+
+- If you place a mutable value (for example, a list or a dictionary) in a
+ session object, and you subsequently mutate that value, you must call the
+ ``changed()`` method of the session object. In this case, the session has no
+ way to know that it was modified. However, when you modify a session object
+ directly, such as setting a value (i.e., ``__setitem__``), or removing a key
+ (e.g., ``del`` or ``pop``), the session will automatically know that it needs
+ to re-serialize its data, thus calling ``changed()`` is unnecessary. There is
+ no harm in calling ``changed()`` in either case, so when in doubt, call it
+ after you've changed sessioning data.
.. index::
single: pyramid_redis_sessions
@@ -183,14 +177,13 @@ pyramid_beaker_ Beaker_ Session factory for Pyramid
Creating Your Own Session Factory
---------------------------------
-If none of the default or otherwise available sessioning
-implementations for :app:`Pyramid` suit you, you may create your own
-session object by implementing a :term:`session factory`. Your
-session factory should return a :term:`session`. The interfaces for
-both types are available in
+If none of the default or otherwise available sessioning implementations for
+:app:`Pyramid` suit you, you may create your own session object by implementing
+a :term:`session factory`. Your session factory should return a
+:term:`session`. The interfaces for both types are available in
:class:`pyramid.interfaces.ISessionFactory` and
-:class:`pyramid.interfaces.ISession`. You might use the cookie
-implementation in the :mod:`pyramid.session` module as inspiration.
+:class:`pyramid.interfaces.ISession`. You might use the cookie implementation
+in the :mod:`pyramid.session` module as inspiration.
.. index::
single: flash messages
@@ -205,9 +198,9 @@ Flash Messages
factory` as described in :ref:`using_the_default_session_factory` or
:ref:`using_alternate_session_factories`.
-Flash messaging has two main uses: to display a status message only once to
-the user after performing an internal redirect, and to allow generic code to
-log messages for single-time display without having direct access to an HTML
+Flash messaging has two main uses: to display a status message only once to the
+user after performing an internal redirect, and to allow generic code to log
+messages for single-time display without having direct access to an HTML
template. The user interface consists of a number of methods of the
:term:`session` object.
@@ -225,7 +218,7 @@ method:
request.session.flash('mymessage')
The ``flash()`` method appends a message to a flash queue, creating the queue
-if necessary.
+if necessary.
``flash()`` accepts three arguments:
@@ -235,22 +228,21 @@ The ``message`` argument is required. It represents a message you wish to
later display to a user. It is usually a string but the ``message`` you
provide is not modified in any way.
-The ``queue`` argument allows you to choose a queue to which to append
-the message you provide. This can be used to push different kinds of
-messages into flash storage for later display in different places on a
-page. You can pass any name for your queue, but it must be a string.
-Each queue is independent, and can be popped by ``pop_flash()`` or
-examined via ``peek_flash()`` separately. ``queue`` defaults to the
-empty string. The empty string represents the default flash message
-queue.
+The ``queue`` argument allows you to choose a queue to which to append the
+message you provide. This can be used to push different kinds of messages into
+flash storage for later display in different places on a page. You can pass
+any name for your queue, but it must be a string. Each queue is independent,
+and can be popped by ``pop_flash()`` or examined via ``peek_flash()``
+separately. ``queue`` defaults to the empty string. The empty string
+represents the default flash message queue.
.. code-block:: python
request.session.flash(msg, 'myappsqueue')
-The ``allow_duplicate`` argument defaults to ``True``. If this is
-``False``, and you attempt to add a message value which is already
-present in the queue, it will not be added.
+The ``allow_duplicate`` argument defaults to ``True``. If this is ``False``,
+and you attempt to add a message value which is already present in the queue,
+it will not be added.
.. index::
single: session.pop_flash
@@ -259,12 +251,12 @@ Using the ``session.pop_flash`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once one or more messages have been added to a flash queue by the
-``session.flash()`` API, the ``session.pop_flash()`` API can be used to
-pop an entire queue and return it for use.
+``session.flash()`` API, the ``session.pop_flash()`` API can be used to pop an
+entire queue and return it for use.
To pop a particular queue of messages from the flash object, use the session
-object's ``pop_flash()`` method. This returns a list of the messages
-that were added to the flash queue, and empties the queue.
+object's ``pop_flash()`` method. This returns a list of the messages that were
+added to the flash queue, and empties the queue.
.. method:: pop_flash(queue='')
@@ -288,10 +280,10 @@ been popped.
Using the ``session.peek_flash`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Once one or more messages has been added to a flash queue by the
-``session.flash()`` API, the ``session.peek_flash()`` API can be used to
-"peek" at that queue. Unlike ``session.pop_flash()``, the queue is not
-popped from flash storage.
+Once one or more messages have been added to a flash queue by the
+``session.flash()`` API, the ``session.peek_flash()`` API can be used to "peek"
+at that queue. Unlike ``session.pop_flash()``, the queue is not popped from
+flash storage.
.. method:: peek_flash(queue='')
@@ -322,8 +314,8 @@ 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
+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`.
.. index::
@@ -342,9 +334,9 @@ 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, then the
-existing token will be returned. If no CSRF token previously existed for
-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.
+existing token will be returned. If no CSRF token previously existed for this
+session, then a new token 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, or supply it as a request
@@ -359,7 +351,7 @@ For example, include the CSRF token as a hidden field:
<input type="submit" value="Delete Everything">
</form>
-Or, include it as a header in a jQuery AJAX request:
+Or include it as a header in a jQuery AJAX request:
.. code-block:: javascript
@@ -372,18 +364,17 @@ Or, include it as a header in a jQuery AJAX request:
alert("Deleted");
});
-
-The handler for the URL that receives the request
-should then require that the correct CSRF token is supplied.
+The handler for the URL that receives the request should then require that the
+correct CSRF token is supplied.
Checking CSRF Tokens Manually
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In request handling code, you can check the presence and validity of a CSRF
-token with :func:`pyramid.session.check_csrf_token`. If the token is
-valid, it will return ``True``, otherwise it will raise ``HTTPBadRequest``.
-Optionally, you can specify ``raises=False`` to have the check return ``False``
-instead of raising an exception.
+token with :func:`pyramid.session.check_csrf_token`. If the token is valid, it
+will return ``True``, otherwise it will raise ``HTTPBadRequest``. Optionally,
+you can specify ``raises=False`` to have the check return ``False`` instead of
+raising an exception.
By default, it checks for a GET or POST parameter named ``csrf_token`` or a
header named ``X-CSRF-Token``.
@@ -401,12 +392,12 @@ header named ``X-CSRF-Token``.
.. index::
single: session.new_csrf_token
-Checking CSRF Tokens With A View Predicate
+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`.
+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_view`.
.. code-block:: python
@@ -414,18 +405,20 @@ See :meth:`pyramid.config.Configurator.add_route`.
def myview(request):
...
+.. note::
+ A mismatch of a CSRF token is treated like any other predicate miss, and the
+ predicate system, when it doesn't find a view, raises ``HTTPNotFound``
+ instead of ``HTTPBadRequest``, so ``check_csrf=True`` behavior is different
+ from calling :func:`pyramid.session.check_csrf_token`.
Using the ``session.new_csrf_token`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-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
-token.
+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 token.
.. code-block:: python
token = request.session.new_csrf_token()
-
-
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index a1a23ed52..485f6b181 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -12,10 +12,9 @@ you'll see something much like this show up on the console:
Starting server in PID 16601.
serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
-This chapter explains what happens between the time you press the "Return"
-key on your keyboard after typing ``pserve development.ini``
-and the time the line ``serving on 0.0.0.0:6543 ...`` is output to your
-console.
+This chapter explains what happens between the time you press the "Return" key
+on your keyboard after typing ``pserve development.ini`` and the time the line
+``serving on 0.0.0.0:6543 ...`` is output to your console.
.. index::
single: startup process
@@ -27,9 +26,8 @@ The Startup Process
The easiest and best-documented way to start and serve a :app:`Pyramid`
application is to use the ``pserve`` command against a :term:`PasteDeploy`
``.ini`` file. This uses the ``.ini`` file to infer settings and starts a
-server listening on a port. For the purposes of this discussion, we'll
-assume that you are using this command to run your :app:`Pyramid`
-application.
+server listening on a port. For the purposes of this discussion, we'll assume
+that you are using this command to run your :app:`Pyramid` application.
Here's a high-level time-ordered overview of what happens when you press
``return`` after running ``pserve development.ini``.
@@ -41,30 +39,29 @@ Here's a high-level time-ordered overview of what happens when you press
#. The framework finds a section named either ``[app:main]``,
``[pipeline:main]``, or ``[composite:main]`` in the ``.ini`` file. This
- section represents the configuration of a :term:`WSGI` application that
- will be served. If you're using a simple application (e.g.
- ``[app:main]``), the application's ``paste.app_factory`` :term:`entry
- point` will be named on the ``use=`` line within the section's
- configuration. If, instead of a simple application, you're using a WSGI
- :term:`pipeline` (e.g. a ``[pipeline:main]`` section), the application
- named on the "last" element will refer to your :app:`Pyramid` application.
- If instead of a simple application or a pipeline, you're using a
- "composite" (e.g. ``[composite:main]``), refer to the documentation for
- that particular composite to understand how to make it refer to your
- :app:`Pyramid` application. In most cases, a Pyramid application built
- from a scaffold will have a single ``[app:main]`` section in it, and this
- will be the application served.
-
-#. The framework finds all :mod:`logging` related configuration in the
- ``.ini`` file and uses it to configure the Python standard library logging
- system for this application. See :ref:`logging_config` for more
- information.
-
-#. The application's *constructor* named by the entry point reference on the
- ``use=`` line of the section representing your :app:`Pyramid` application
- is passed the key/value parameters mentioned within the section in which
- it's defined. The constructor is meant to return a :term:`router`
- instance, which is a :term:`WSGI` application.
+ section represents the configuration of a :term:`WSGI` application that will
+ be served. If you're using a simple application (e.g., ``[app:main]``), the
+ application's ``paste.app_factory`` :term:`entry point` will be named on the
+ ``use=`` line within the section's configuration. If instead of a simple
+ application, you're using a WSGI :term:`pipeline` (e.g., a
+ ``[pipeline:main]`` section), the application named on the "last" element
+ will refer to your :app:`Pyramid` application. If instead of a simple
+ application or a pipeline, you're using a "composite" (e.g.,
+ ``[composite:main]``), refer to the documentation for that particular
+ composite to understand how to make it refer to your :app:`Pyramid`
+ application. In most cases, a Pyramid application built from a scaffold
+ will have a single ``[app:main]`` section in it, and this will be the
+ application served.
+
+#. The framework finds all :mod:`logging` related configuration in the ``.ini``
+ file and uses it to configure the Python standard library logging system for
+ this application. See :ref:`logging_config` for more information.
+
+#. The application's *constructor* named by the entry point referenced on the
+ ``use=`` line of the section representing your :app:`Pyramid` application is
+ passed the key/value parameters mentioned within the section in which it's
+ defined. The constructor is meant to return a :term:`router` instance,
+ which is a :term:`WSGI` application.
For :app:`Pyramid` applications, the constructor will be a function named
``main`` in the ``__init__.py`` file within the :term:`package` in which
@@ -78,14 +75,13 @@ Here's a high-level time-ordered overview of what happens when you press
Note that the constructor function accepts a ``global_config`` argument,
which is a dictionary of key/value pairs mentioned in the ``[DEFAULT]``
- section of an ``.ini`` file
- (if :ref:`[DEFAULT] <defaults_section_of_pastedeploy_file>` is present).
- It also accepts a ``**settings`` argument, which collects
- another set of arbitrary key/value pairs. The arbitrary key/value pairs
- received by this function in ``**settings`` will be composed of all the
- key/value pairs that are present in the ``[app:main]`` section (except for
- the ``use=`` setting) when this function is called by when you run
- ``pserve``.
+ section of an ``.ini`` file (if :ref:`[DEFAULT]
+ <defaults_section_of_pastedeploy_file>` is present). It also accepts a
+ ``**settings`` argument, which collects another set of arbitrary key/value
+ pairs. The arbitrary key/value pairs received by this function in
+ ``**settings`` will be composed of all the key/value pairs that are present
+ in the ``[app:main]`` section (except for the ``use=`` setting) when this
+ function is called when you run ``pserve``.
Our generated ``development.ini`` file looks like so:
@@ -95,8 +91,8 @@ Here's a high-level time-ordered overview of what happens when you press
In this case, the ``myproject.__init__:main`` function referred to by the
entry point URI ``egg:MyProject`` (see :ref:`MyProject_ini` for more
- information about entry point URIs, and how they relate to callables),
- will receive the key/value pairs ``{'pyramid.reload_templates':'true',
+ information about entry point URIs, and how they relate to callables) will
+ receive the key/value pairs ``{'pyramid.reload_templates':'true',
'pyramid.debug_authorization':'false', 'pyramid.debug_notfound':'false',
'pyramid.debug_routematch':'false', 'pyramid.debug_templates':'true',
'pyramid.default_locale_name':'en'}``. See :ref:`environment_chapter` for
@@ -114,13 +110,13 @@ Here's a high-level time-ordered overview of what happens when you press
#. The ``main`` function then calls various methods on the instance of the
class :class:`~pyramid.config.Configurator` created in the previous step.
- The intent of calling these methods is to populate an
- :term:`application registry`, which represents the :app:`Pyramid`
- configuration related to the application.
+ The intent of calling these methods is to populate an :term:`application
+ registry`, which represents the :app:`Pyramid` configuration related to the
+ application.
-#. The :meth:`~pyramid.config.Configurator.make_wsgi_app` method is called.
- The result is a :term:`router` instance. The router is associated with
- the :term:`application registry` implied by the configurator previously
+#. The :meth:`~pyramid.config.Configurator.make_wsgi_app` method is called. The
+ result is a :term:`router` instance. The router is associated with the
+ :term:`application registry` implied by the configurator previously
populated by other methods run against the Configurator. The router is a
WSGI application.
@@ -141,11 +137,10 @@ Here's a high-level time-ordered overview of what happens when you press
to receive requests.
.. seealso::
- Logging configuration is described in the :ref:`logging_chapter`
- chapter. There, in :ref:`request_logging_with_pastes_translogger`,
- you will also find an example of how to configure
- :term:`middleware` to add pre-packaged functionality to your
- application.
+ Logging configuration is described in the :ref:`logging_chapter` chapter.
+ There, in :ref:`request_logging_with_pastes_translogger`, you will also find
+ an example of how to configure :term:`middleware` to add pre-packaged
+ functionality to your application.
.. index::
pair: settings; deployment
@@ -158,8 +153,7 @@ Deployment Settings
Note that an augmented version of the values passed as ``**settings`` to the
:class:`~pyramid.config.Configurator` constructor will be available in
-:app:`Pyramid` :term:`view callable` code as ``request.registry.settings``.
-You can create objects you wish to access later from view code, and put them
-into the dictionary you pass to the configurator as ``settings``. They will
-then be present in the ``request.registry.settings`` dictionary at
-application runtime.
+:app:`Pyramid` :term:`view callable` code as ``request.registry.settings``. You
+can create objects you wish to access later from view code, and put them into
+the dictionary you pass to the configurator as ``settings``. They will then be
+present in the ``request.registry.settings`` dictionary at application runtime.
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 4c1364493..9e3a31845 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -3,15 +3,14 @@
Templates
=========
-A :term:`template` is a file on disk which can be used to render
-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.
+A :term:`template` is a file on disk which can be used to render 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.
-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
-configuration.
+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 configuration.
.. index::
single: templates used directly
@@ -21,16 +20,15 @@ configuration.
Using Templates Directly
------------------------
-The most straightforward way to use a template within
-:app:`Pyramid` is to cause it to be rendered directly within a
-:term:`view callable`. You may use whatever API is supplied by a
-given templating engine to do so.
+The most straightforward way to use a template within :app:`Pyramid` is to
+cause it to be rendered directly within a :term:`view callable`. You may use
+whatever API is supplied by a 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
-``templates`` in your application, you can render the template from
-within the body of a view callable like 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 ``templates`` in your
+application, you can render the template from within the body of a view
+callable like so:
.. code-block:: python
:linenos:
@@ -43,23 +41,21 @@ within the body of a view callable like so:
request=request)
The ``sample_view`` :term:`view callable` function above returns a
-:term:`response` object which contains the body of the
-``templates/foo.pt`` template. In this case, the ``templates``
-directory should live in the same directory as the module containing
-the ``sample_view`` function. The template author will have the names
-``foo`` and ``bar`` available as top-level names for replacement or
-comparison purposes.
+:term:`response` object which contains the body of the ``templates/foo.pt``
+template. In this case, the ``templates`` directory should live in the same
+directory as the module containing the ``sample_view`` function. The template
+author will have the names ``foo`` and ``bar`` available as top-level names for
+replacement or comparison purposes.
In the example above, the path ``templates/foo.pt`` is relative to the
-directory containing the file which defines the view configuration.
-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. The path can alternately be an
-: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:
+directory containing the file which defines the view configuration. 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. The path can alternatively
+be an :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:
.. code-block:: python
:linenos:
@@ -71,38 +67,36 @@ address template assets which live in another package. For example:
{'foo':1, 'bar':2},
request=request)
-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 an
-asset specification instead of a relative template name is usually
-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 to another location.
+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 an asset specification instead of a
+relative template name is usually 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 to another
+location.
In the examples above we pass in a keyword argument named ``request``
-representing the current :app:`Pyramid` request. Passing a request
-keyword argument will cause the ``render_to_response`` function to
-supply the renderer with more correct system values (see
-:ref:`renderer_system_values`), because most of the information required
-to compose proper system values is present in the request. If your
-template relies on the name ``request`` or ``context``, or if you've
-configured special :term:`renderer globals`, make sure to pass
+representing the current :app:`Pyramid` request. Passing a request keyword
+argument will cause the ``render_to_response`` function to supply the renderer
+with more correct system values (see :ref:`renderer_system_values`), because
+most of the information required to compose proper system values is present in
+the request. If your template relies on the name ``request`` or ``context``,
+or if you've configured special :term:`renderer globals`, make sure to pass
``request`` as a keyword argument in every call to a
``pyramid.renderers.render_*`` function.
-Every view must return a :term:`response` object, except for views
-which use a :term:`renderer` named via view configuration (which we'll
-see shortly). The :func:`pyramid.renderers.render_to_response`
-function is a shortcut function that actually returns a response
-object. This allows the example view above to simply return the result
-of its call to ``render_to_response()`` directly.
+Every view must return a :term:`response` object, except for views which use a
+:term:`renderer` named via view configuration (which we'll see shortly). The
+:func:`pyramid.renderers.render_to_response` function is a shortcut function
+that actually returns a response object. This allows the example view above to
+simply return the result of its call to ``render_to_response()`` directly.
Obviously not all APIs you might call to get response data will return a
-response object. For example, you might render one or more templates to
-a string that you want to use as response data. The
-:func:`pyramid.renderers.render` API renders a template to a string. We
-can manufacture a :term:`response` object directly, and use that string
-as the body of the response:
+response object. For example, you might render one or more templates to a
+string that you want to use as response data. The
+:func:`pyramid.renderers.render` API renders a template to a string. We can
+manufacture a :term:`response` object directly, and use that string as the body
+of the response:
.. code-block:: python
:linenos:
@@ -119,10 +113,10 @@ as the body of the response:
Because :term:`view callable` functions are typically the only code in
:app:`Pyramid` that need to know anything about templates, and because view
-functions are very simple Python, you can use whatever templating system you're
-most comfortable with within :app:`Pyramid`. Install the templating system,
-import its API functions into your views module, use those APIs to generate a
-string, then return that string as the body of a :app:`Pyramid`
+functions are very simple Python, you can use whatever templating system with
+which you're most comfortable within :app:`Pyramid`. Install the templating
+system, import its API functions into your views module, use those APIs to
+generate a string, then return that string as the body of a :app:`Pyramid`
:term:`Response` object.
For example, here's an example of using "raw" Mako_ from within a
@@ -141,34 +135,32 @@ For example, here's an example of using "raw" Mako_ from within a
return response
You probably wouldn't use this particular snippet in a project, because it's
-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.
+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::
If you use third-party templating languages without cooperating
:app:`Pyramid` bindings directly within view callables, the
- auto-template-reload strategy explained in
- :ref:`reload_templates_section` will not be available, nor will the
- template asset overriding capability explained in
- :ref:`overriding_assets_section` be available, nor will it be
- possible to use any template using that language as a
- :term:`renderer`. However, it's reasonably easy to write custom
- templating system binding packages for use under :app:`Pyramid` so
- that templates written in the language can be used as renderers.
- See :ref:`adding_and_overriding_renderers` for instructions on how
- to create your own template renderer and
- :ref:`available_template_system_bindings` for example packages.
-
-If you need more control over the status code and content-type, or
-other response attributes from views that use direct templating, you
-may set attributes on the response that influence these values.
-
-Here's an example of changing the content-type and status of the
-response object returned by
-:func:`~pyramid.renderers.render_to_response`:
+ auto-template-reload strategy explained in :ref:`reload_templates_section`
+ will not be available, nor will the template asset overriding capability
+ explained in :ref:`overriding_assets_section` be available, nor will it be
+ possible to use any template using that language as a :term:`renderer`.
+ However, it's reasonably easy to write custom templating system binding
+ packages for use under :app:`Pyramid` so that templates written in the
+ language can be used as renderers. See
+ :ref:`adding_and_overriding_renderers` for instructions on how to create
+ your own template renderer and :ref:`available_template_system_bindings`
+ for example packages.
+
+If you need more control over the status code and content-type, or other
+response attributes from views that use direct templating, you may set
+attributes on the response that influence these values.
+
+Here's an example of changing the content-type and status of the response
+object returned by :func:`~pyramid.renderers.render_to_response`:
.. code-block:: python
:linenos:
@@ -183,8 +175,8 @@ response object returned by
response.status_int = 204
return response
-Here's an example of manufacturing a response object using the result
-of :func:`~pyramid.renderers.render` (a string):
+Here's an example of manufacturing a response object using the result of
+:func:`~pyramid.renderers.render` (a string):
.. code-block:: python
:linenos:
@@ -214,9 +206,8 @@ of :func:`~pyramid.renderers.render` (a string):
System Values Used During Rendering
-----------------------------------
-When a template is rendered using
-:func:`~pyramid.renderers.render_to_response` or
-:func:`~pyramid.renderers.render`, or a ``renderer=`` argument to view
+When a template is rendered using :func:`~pyramid.renderers.render_to_response`
+or :func:`~pyramid.renderers.render`, or a ``renderer=`` argument to view
configuration (see :ref:`templates_used_as_renderers`), the renderer
representing the template will be provided with a number of *system* values.
These values are provided to the template:
@@ -232,31 +223,31 @@ These values are provided to the template:
``context``
The current :app:`Pyramid` :term:`context` if ``request`` was provided as a
- keyword argument to ``render_to_response`` or ``render``, or ``None`` if
- the ``request`` keyword argument was not provided. This value will always
- be provided if the template is rendered as the result of a ``renderer=``
- argument to view configuration being used.
+ keyword argument to ``render_to_response`` or ``render``, or ``None`` if the
+ ``request`` keyword argument was not provided. This value will always be
+ provided if the template is rendered as the result of a ``renderer=``
+ argument to the view configuration being used.
``renderer_name``
- The renderer name used to perform the rendering,
- e.g. ``mypackage:templates/foo.pt``.
+ The renderer name used to perform the rendering, e.g.,
+ ``mypackage:templates/foo.pt``.
``renderer_info``
An object implementing the :class:`pyramid.interfaces.IRendererInfo`
interface. Basically, an object with the following attributes: ``name``,
- ``package`` and ``type``.
+ ``package``, and ``type``.
``view``
- The view callable object that was used to render this template. If the
- view callable is a method of a class-based view, this will be an instance
- of the class that the method was defined on. If the view callable is a
- function or instance, it will be that function or instance. Note that this
- value will only be automatically present when a template is rendered as a
- result of a ``renderer=`` argument; it will be ``None`` when the
- ``render_to_response`` or ``render`` APIs are used.
+ The view callable object that was used to render this template. If the view
+ callable is a method of a class-based view, this will be an instance of the
+ class that the method was defined on. If the view callable is a function or
+ instance, it will be that function or instance. Note that this value will
+ only be automatically present when a template is rendered as a result of a
+ ``renderer=`` argument; it will be ``None`` when the ``render_to_response``
+ or ``render`` APIs are used.
-You can define more values which will be passed to every template executed as
-a result of rendering by defining :term:`renderer globals`.
+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 make these names available as
@@ -270,26 +261,23 @@ top-level template variables.
Templates Used as Renderers via Configuration
---------------------------------------------
-An alternative to using :func:`~pyramid.renderers.render_to_response`
-to render templates manually in your view callable code, is
-to specify the template as a :term:`renderer` in your
-*view configuration*. This can be done with any of the
+An alternative to using :func:`~pyramid.renderers.render_to_response` to render
+templates manually in your view callable code is to specify the template as a
+:term:`renderer` in your *view configuration*. This can be done with any of the
templating languages supported by :app:`Pyramid`.
-To use a renderer via view configuration, specify a template
-:term:`asset specification` as the ``renderer`` argument, or
-attribute to the :term:`view configuration` of a :term:`view
-callable`. Then return a *dictionary* from that view callable. The
-dictionary items returned by the view callable will be made available
-to the renderer template as top-level names.
+To use a renderer via view configuration, specify a template :term:`asset
+specification` as the ``renderer`` argument, or attribute to the :term:`view
+configuration` of a :term:`view callable`. Then return a *dictionary* from
+that view callable. The dictionary items returned by the view callable will be
+made available to the renderer template as top-level names.
-The association of a template as a renderer for a :term:`view
-configuration` makes it possible to replace code within a :term:`view
-callable` that handles the rendering of a template.
+The association of a template as a renderer for a :term:`view configuration`
+makes it possible to replace code within a :term:`view callable` that handles
+the rendering of a template.
-Here's an example of using a :class:`~pyramid.view.view_config`
-decorator to specify a :term:`view configuration` that names a
-template renderer:
+Here's an example of using a :class:`~pyramid.view.view_config` decorator to
+specify a :term:`view configuration` that names a template renderer:
.. code-block:: python
:linenos:
@@ -302,11 +290,10 @@ template renderer:
.. 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
- the renderer.
+ 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 the renderer.
.. warning::
@@ -314,7 +301,7 @@ template renderer:
shown above is the template *path*. In the example above, the path
``templates/foo.pt`` is *relative*. Relative to what, you ask? Because
we're using a Chameleon renderer, it means "relative to the directory in
- which the file which defines the view configuration lives". In this case,
+ which the file that defines the view configuration lives". In this case,
this is the directory containing the file that defines the ``my_view``
function.
@@ -327,7 +314,7 @@ Similar renderer configuration can be done imperatively. See
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. The path can alternately be an :term:`asset
+letter prefix on Windows. The path can alternatively be an :term:`asset
specification` in the form ``some.dotted.package_name:relative/path``, making
it possible to address template assets which live in another package.
@@ -335,32 +322,31 @@ 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.
-.. sidebar:: Why Use A Renderer via View Configuration
-
- Using a renderer in view configuration is usually a better way to
- render templates than using any rendering API directly from within a
- :term:`view callable` because it makes the view callable more
- unit-testable. Views which use templating or rendering APIs directly
- must return a :term:`Response` object. Making testing assertions
- about response objects is typically an indirect process, because it
- means that your test code often needs to somehow parse information
- out of the response body (often HTML). View callables configured
- with renderers externally via view configuration typically return a
- dictionary, as above. Making assertions about results returned in a
- dictionary is almost always more direct and straightforward than
- needing to parse HTML.
+.. sidebar:: Why Use a Renderer via View Configuration
+
+ Using a renderer in view configuration is usually a better way to render
+ templates than using any rendering API directly from within a :term:`view
+ callable` because it makes the view callable more unit-testable. Views
+ which use templating or rendering APIs directly must return a
+ :term:`Response` object. Making testing assertions about response objects
+ is typically an indirect process, because it means that your test code often
+ needs to somehow parse information out of the response body (often HTML).
+ View callables configured with renderers externally via view configuration
+ typically return a dictionary, as above. Making assertions about results
+ returned in a dictionary is almost always more direct and straightforward
+ than needing to parse HTML.
By default, views rendered via a template renderer return a :term:`Response`
object which has a *status code* of ``200 OK``, and a *content-type* of
``text/html``. To vary attributes of the response of a view that uses a
-renderer, such as the content-type, headers, or status attributes, you must
-use the API of the :class:`pyramid.response.Response` object exposed as
+renderer, such as the content-type, headers, or status attributes, you must use
+the API of the :class:`pyramid.response.Response` object exposed as
``request.response`` within the view before returning the dictionary. See
:ref:`request_response_attr` for more information.
-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`.
+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::
pair: debugging; templates
@@ -401,32 +387,31 @@ displaying the arguments passed to the template itself.
Automatically Reloading Templates
---------------------------------
-It's often convenient to see changes you make to a template file
-appear immediately without needing to restart the application process.
-:app:`Pyramid` allows you to configure your application development
-environment so that a change to a template will be automatically
-detected, and the template will be reloaded on the next rendering.
+It's often convenient to see changes you make to a template file appear
+immediately without needing to restart the application process. :app:`Pyramid`
+allows you to configure your application development 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.
+ 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.
+environment variable or a configuration file setting.
-To use an environment variable, start your application under a shell
-using the ``PYRAMID_RELOAD_TEMPLATES`` operating system environment
-variable set to ``1``, For example:
+To use an environment variable, start your application under a shell using the
+``PYRAMID_RELOAD_TEMPLATES`` operating system environment variable set to
+``1``, For example:
.. code-block:: text
$ 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.:
+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:
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index fa3e734fe..c13558008 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -8,7 +8,7 @@ URL Dispatch
:term:`URL dispatch` provides a simple way to map URLs to :term:`view` code
using a simple pattern matching language. An ordered set of patterns is
-checked one-by-one. If one of the patterns matches the path information
+checked one by one. If one of the patterns matches the path information
associated with a request, a particular :term:`view callable` is invoked. A
view callable is a specific bit of code, defined in your application, that
receives the :term:`request` and returns a :term:`response` object.
@@ -21,12 +21,12 @@ If any route configuration is present in an application, the :app:`Pyramid`
matching patterns present in a *route map*.
If any route pattern matches the information in the :term:`request`,
-:app:`Pyramid` will invoke the :term:`view lookup` process to find a
-matching view.
+:app:`Pyramid` will invoke the :term:`view lookup` process to find a matching
+view.
If no route pattern in the route map matches the information in the
-:term:`request` provided in your application, :app:`Pyramid` will fail over
-to using :term:`traversal` to perform resource location and view lookup.
+:term:`request` provided in your application, :app:`Pyramid` will fail over to
+using :term:`traversal` to perform resource location and view lookup.
.. index::
single: route configuration
@@ -35,11 +35,11 @@ Route Configuration
-------------------
:term:`Route configuration` is the act of adding a new :term:`route` to an
-application. A route has a *name*, which acts as an identifier to be used
-for URL generation. The name also allows developers to associate a view
+application. A route has a *name*, which acts as an identifier to be used for
+URL generation. The name also allows developers to associate a view
configuration with the route. A route also has a *pattern*, meant to match
against the ``PATH_INFO`` portion of a URL (the portion following the scheme
-and port, e.g. ``/foo/bar`` in the URL `<http://localhost:8080/foo/bar>`_). It
+and port, e.g., ``/foo/bar`` in the URL ``http://localhost:8080/foo/bar``). It
also optionally has a ``factory`` and a set of :term:`route predicate`
attributes.
@@ -71,8 +71,8 @@ invoked when the associated route pattern matches during a request.
More commonly, you will not use any ``add_view`` statements in your project's
"setup" code. You will instead use ``add_route`` statements, and use a
-:term:`scan` to associate view callables with routes. For example, if
-this is a portion of your project's ``__init__.py``:
+:term:`scan` to associate view callables with routes. For example, if this is
+a portion of your project's ``__init__.py``:
.. code-block:: python
@@ -85,8 +85,8 @@ setup code. However, the above :term:`scan` execution
decoration`, including any objects decorated with the
:class:`pyramid.view.view_config` decorator in the ``mypackage`` Python
package. For example, if you have a ``views.py`` in your package, a scan will
-pick up any of its configuration decorators, so we can add one there
-that references ``myroute`` as a ``route_name`` parameter:
+pick up any of its configuration decorators, so we can add one there that
+references ``myroute`` as a ``route_name`` parameter:
.. code-block:: python
@@ -97,8 +97,8 @@ that references ``myroute`` as a ``route_name`` parameter:
def myview(request):
return Response('OK')
-The above combination of ``add_route`` and ``scan`` is completely equivalent
-to using the previous combination of ``add_route`` and ``add_view``.
+The above combination of ``add_route`` and ``scan`` is completely equivalent to
+using the previous combination of ``add_route`` and ``add_view``.
.. index::
single: route path pattern syntax
@@ -109,13 +109,13 @@ to using the previous combination of ``add_route`` and ``add_view``.
Route Pattern Syntax
~~~~~~~~~~~~~~~~~~~~
-The syntax of the pattern matching language used by :app:`Pyramid` URL
-dispatch in the *pattern* argument is straightforward; it is close to that of
-the :term:`Routes` system used by :term:`Pylons`.
+The syntax of the pattern matching language used by :app:`Pyramid` URL dispatch
+in the *pattern* argument is straightforward. It is close to that of the
+:term:`Routes` system used by :term:`Pylons`.
-The *pattern* used in route configuration may start with a slash character.
-If the pattern does not start with a slash character, an implicit slash will
-be prepended to it at matching time. For example, the following patterns are
+The *pattern* used in route configuration may start with a slash character. If
+the pattern does not start with a slash character, an implicit slash will be
+prepended to it at matching time. For example, the following patterns are
equivalent:
.. code-block:: text
@@ -128,28 +128,29 @@ 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.
+If a pattern is a valid URL it won't be 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
-replacement marker does not need to be preceded by a ``/`` character.
+A pattern segment (an individual item between ``/`` characters in the pattern)
+may either be a literal string (e.g., ``foo``) *or* it may be a replacement
+marker (e.g., ``{foo}``), or a certain combination of both. A replacement
+marker does not need to be preceded by a ``/`` character.
-A replacement marker is in the format ``{name}``, where this means "accept
-any characters up to the next slash character and use this as the ``name``
+A replacement marker is in the format ``{name}``, where this means "accept any
+characters up to the next slash character and use this as the ``name``
:term:`matchdict` value."
A replacement marker in a pattern must begin with an uppercase or lowercase
ASCII letter or an underscore, and can be composed only of uppercase or
lowercase ASCII letters, underscores, and numbers. For example: ``a``,
-``a_b``, ``_b``, and ``b9`` are all valid replacement marker names, but
-``0a`` is not.
+``a_b``, ``_b``, and ``b9`` are all valid replacement marker names, but ``0a``
+is not.
-.. note:: A replacement marker could not start with an underscore until
- Pyramid 1.2. Previous versions required that the replacement marker start
- with an uppercase or lowercase letter.
+.. versionchanged:: 1.2
+ A replacement marker could not start with an underscore until Pyramid 1.2.
+ Previous versions required that the replacement marker start with an
+ uppercase or lowercase letter.
A matchdict is the dictionary representing the dynamic parts extracted from a
URL based on the routing pattern. It is available as ``request.matchdict``.
@@ -174,18 +175,18 @@ It will not match the following patterns however:
foo/1/2/ -> No match (trailing slash)
bar/abc/def -> First segment literal mismatch
-The match for a segment replacement marker in a segment will be done only up
-to the first non-alphanumeric character in the segment in the pattern. So,
-for instance, if this route pattern was used:
+The match for a segment replacement marker in a segment will be done only up to
+the first non-alphanumeric character in the segment in the pattern. So, for
+instance, if this route pattern was used:
.. code-block:: text
foo/{name}.html
-The literal path ``/foo/biz.html`` will match the above route pattern, and
-the match result will be ``{'name':u'biz'}``. However, the literal path
-``/foo/biz`` will not match, because it does not contain a literal ``.html``
-at the end of the segment represented by ``{name}.html`` (it only contains
+The literal path ``/foo/biz.html`` will match the above route pattern, and the
+match result will be ``{'name':u'biz'}``. However, the literal path
+``/foo/biz`` will not match, because it does not contain a literal ``.html`` at
+the end of the segment represented by ``{name}.html`` (it only contains
``biz``, not ``biz.html``).
To capture both segments, two replacement markers can be used:
@@ -194,28 +195,27 @@ To capture both segments, two replacement markers can be used:
foo/{name}.{ext}
-The literal path ``/foo/biz.html`` will match the above route pattern, and
-the match result will be ``{'name': 'biz', 'ext': 'html'}``. This occurs
-because there is a literal part of ``.`` (period) between the two replacement
-markers ``{name}`` and ``{ext}``.
+The literal path ``/foo/biz.html`` will match the above route pattern, and the
+match result will be ``{'name': 'biz', 'ext': 'html'}``. This occurs because
+there is a literal part of ``.`` (period) between the two replacement markers
+``{name}`` and ``{ext}``.
Replacement markers can optionally specify a regular expression which will be
-used to decide whether a path segment should match the marker. To specify
-that a replacement marker should match only a specific set of characters as
-defined by a regular expression, you must use a slightly extended form of
-replacement marker syntax. Within braces, the replacement marker name must
-be followed by a colon, then directly thereafter, the regular expression.
-The *default* regular expression associated with a replacement marker
-``[^/]+`` matches one or more characters which are not a slash. For example,
-under the hood, the replacement marker ``{foo}`` can more verbosely be
-spelled as ``{foo:[^/]+}``. You can change this to be an arbitrary regular
-expression to match an arbitrary sequence of characters, such as
-``{foo:\d+}`` to match only digits.
+used to decide whether a path segment should match the marker. To specify that
+a replacement marker should match only a specific set of characters as defined
+by a regular expression, you must use a slightly extended form of replacement
+marker syntax. Within braces, the replacement marker name must be followed by
+a colon, then directly thereafter, the regular expression. The *default*
+regular expression associated with a replacement marker ``[^/]+`` matches one
+or more characters which are not a slash. For example, under the hood, the
+replacement marker ``{foo}`` can more verbosely be spelled as ``{foo:[^/]+}``.
+You can change this to be an arbitrary regular expression to match an arbitrary
+sequence of characters, such as ``{foo:\d+}`` to match only digits.
It is possible to use two replacement markers without any literal characters
between them, for instance ``/{foo}{bar}``. However, this would be a
-nonsensical pattern without specifying a custom regular expression to
-restrict what each marker captures.
+nonsensical pattern without specifying a custom regular expression to restrict
+what each marker captures.
Segments must contain at least one character in order to match a segment
replacement marker. For example, for the URL ``/abc/``:
@@ -224,7 +224,7 @@ replacement marker. For example, for the URL ``/abc/``:
- ``/{foo}/`` will match.
-Note that values representing matched path segments will be url-unquoted and
+Note that values representing matched path segments will be URL-unquoted and
decoded from UTF-8 into Unicode within the matchdict. So for instance, the
following pattern:
@@ -244,9 +244,9 @@ The matchdict will look like so (the value is URL-decoded / UTF-8 decoded):
{'bar':u'La Pe\xf1a'}
-Literal strings in the path segment should represent the *decoded* value of
-the ``PATH_INFO`` provided to Pyramid. You don't want to use a URL-encoded
-value or a bytestring representing the literal's UTF-8 in the pattern. For
+Literal strings in the path segment should represent the *decoded* value of the
+``PATH_INFO`` provided to Pyramid. You don't want to use a URL-encoded value
+or a bytestring representing the literal encoded as UTF-8 in the pattern. For
example, rather than this:
.. code-block:: text
@@ -259,8 +259,8 @@ You'll want to use something like this:
/Foo Bar/{baz}
-For patterns that contain "high-order" characters in its literals, you'll
-want to use a Unicode value as the pattern as opposed to any URL-encoded or
+For patterns that contain "high-order" characters in its literals, you'll want
+to use a Unicode value as the pattern as opposed to any URL-encoded or
UTF-8-encoded value. For example, you might be tempted to use a bytestring
pattern like this:
@@ -268,12 +268,11 @@ pattern like this:
/La Pe\xc3\xb1a/{x}
-But this will either cause an error at startup time or it won't match
-properly. You'll want to use a Unicode value as the pattern instead rather
-than raw bytestring escapes. You can use a high-order Unicode value as the
-pattern by using `Python source file encoding
-<http://www.python.org/dev/peps/pep-0263/>`_ plus the "real" character in the
-Unicode pattern in the source, like so:
+But this will either cause an error at startup time or it won't match properly.
+You'll want to use a Unicode value as the pattern instead rather than raw
+bytestring escapes. You can use a high-order Unicode value as the pattern by
+using `Python source file encoding <http://www.python.org/dev/peps/pep-0263/>`_
+plus the "real" character in the Unicode pattern in the source, like so:
.. code-block:: text
@@ -291,8 +290,8 @@ only to literals in the pattern.
If the pattern has a ``*`` in it, the name which follows it is considered a
"remainder match". A remainder match *must* come at the end of the pattern.
-Unlike segment replacement markers, it does not need to be preceded by a
-slash. For example:
+Unlike segment replacement markers, it does not need to be preceded by a slash.
+For example:
.. code-block:: text
@@ -310,7 +309,7 @@ The above pattern will match these URLs, generating the following matchdicts:
Note that when a ``*stararg`` remainder match is matched, the value put into
the matchdict is turned into a tuple of path segments representing the
-remainder of the path. These path segments are url-unquoted and decoded from
+remainder of the path. These path segments are URL-unquoted and decoded from
UTF-8 into Unicode. For example, for the following pattern:
.. code-block:: text
@@ -357,15 +356,15 @@ Route Declaration Ordering
Route configuration declarations are evaluated in a specific order when a
request enters the system. As a result, the order of route configuration
-declarations is very important. The order that routes declarations are
+declarations is very important. The order in which route declarations are
evaluated is the order in which they are added to the application at startup
time. (This is unlike a different way of mapping URLs to code that
:app:`Pyramid` provides, named :term:`traversal`, which does not depend on
pattern ordering).
For routes added via the :mod:`~pyramid.config.Configurator.add_route` method,
-the order that routes are evaluated is the order in which they are added to
-the configuration imperatively.
+the order that routes are evaluated is the order in which they are added to the
+configuration imperatively.
For example, route configuration statements with the following patterns might
be added in the following order:
@@ -375,10 +374,9 @@ be added in the following order:
members/{def}
members/abc
-In such a configuration, the ``members/abc`` pattern would *never* be
-matched. This is because the match ordering will always match
-``members/{def}`` first; the route configuration with ``members/abc`` will
-never be evaluated.
+In such a configuration, the ``members/abc`` pattern would *never* be matched.
+This is because the match ordering will always match ``members/{def}`` first;
+the route configuration with ``members/abc`` will never be evaluated.
.. index::
single: route configuration arguments
@@ -416,19 +414,18 @@ the system, for each route configuration declaration present in the system,
declared. This checking happens in the order that the routes were declared
via :meth:`pyramid.config.Configurator.add_route`.
-When a route configuration is declared, it may contain :term:`route
-predicate` arguments. All route predicates associated with a route
-declaration must be ``True`` for the route configuration to be used for a
-given request during a check. If any predicate in the set of :term:`route
-predicate` arguments provided to a route configuration returns ``False``
-during a check, that route is skipped and route matching continues through
-the ordered set of routes.
+When a route configuration is declared, it may contain :term:`route predicate`
+arguments. All route predicates associated with a route declaration must be
+``True`` for the route configuration to be used for a given request during a
+check. If any predicate in the set of :term:`route predicate` arguments
+provided to a route configuration returns ``False`` during a check, that route
+is skipped and route matching continues through the ordered set of routes.
If any route matches, the route matching process stops and the :term:`view
-lookup` subsystem takes over to find the most reasonable view callable for
-the matched route. Most often, there's only one view that will match (a view
-configured with a ``route_name`` argument matching the matched route). To
-gain a better understanding of how routes and views are associated in a real
+lookup` subsystem takes over to find the most reasonable view callable for the
+matched route. Most often, there's only one view that will match (a view
+configured with a ``route_name`` argument matching the matched route). To gain
+a better understanding of how routes and views are associated in a real
application, you can use the ``pviews`` command, as documented in
:ref:`displaying_matching_views`.
@@ -445,11 +442,10 @@ The Matchdict
~~~~~~~~~~~~~
When the URL pattern associated with a particular route configuration is
-matched by a request, a dictionary named ``matchdict`` is added as an
-attribute of the :term:`request` object. Thus, ``request.matchdict`` will
-contain the values that match replacement patterns in the ``pattern``
-element. The keys in a matchdict will be strings. The values will be
-Unicode objects.
+matched by a request, a dictionary named ``matchdict`` is added as an attribute
+of the :term:`request` object. Thus, ``request.matchdict`` will contain the
+values that match replacement patterns in the ``pattern`` element. The keys in
+a matchdict will be strings. The values will be Unicode objects.
.. note::
@@ -466,10 +462,10 @@ The Matched Route
When the URL pattern associated with a particular route configuration is
matched by a request, an object named ``matched_route`` is added as an
-attribute of the :term:`request` object. Thus, ``request.matched_route``
-will be an object implementing the :class:`~pyramid.interfaces.IRoute`
-interface which matched the request. The most useful attribute of the route
-object is ``name``, which is the name of the route that matched.
+attribute of the :term:`request` object. Thus, ``request.matched_route`` will
+be an object implementing the :class:`~pyramid.interfaces.IRoute` interface
+which matched the request. The most useful attribute of the route object is
+``name``, which is the name of the route that matched.
.. note::
@@ -480,8 +476,8 @@ Routing Examples
----------------
Let's check out some examples of how route configuration statements might be
-commonly declared, and what will happen if they are matched by the
-information present in a request.
+commonly declared, and what will happen if they are matched by the information
+present in a request.
.. _urldispatch_example1:
@@ -505,18 +501,18 @@ configuration will be invoked.
Recall that the ``@view_config`` is equivalent to calling ``config.add_view``,
because the ``config.scan()`` call will import ``mypackage.views``, shown
below, and execute ``config.add_view`` under the hood. Each view then maps the
-route name to the matching view callable. In the case of the above
-example, when the URL of a request matches ``/site/{id}``, the view callable at
-the Python dotted path name ``mypackage.views.site_view`` will be called with
-the request. In other words, we've associated a view callable directly with a
+route name to the matching view callable. In the case of the above example,
+when the URL of a request matches ``/site/{id}``, the view callable at the
+Python dotted path name ``mypackage.views.site_view`` will be called with the
+request. In other words, we've associated a view callable directly with a
route pattern.
When the ``/site/{id}`` route pattern matches during a request, the
-``site_view`` view callable is invoked with that request as its sole
-argument. When this route matches, a ``matchdict`` will be generated and
-attached to the request as ``request.matchdict``. If the specific URL
-matched is ``/site/1``, the ``matchdict`` will be a dictionary with a single
-key, ``id``; the value will be the string ``'1'``, ex.: ``{'id':'1'}``.
+``site_view`` view callable is invoked with that request as its sole argument.
+When this route matches, a ``matchdict`` will be generated and attached to the
+request as ``request.matchdict``. If the specific URL matched is ``/site/1``,
+the ``matchdict`` will be a dictionary with a single key, ``id``; the value
+will be the string ``'1'``, ex.: ``{'id':'1'}``.
The ``mypackage.views`` module referred to above might look like so:
@@ -539,8 +535,8 @@ information about views.
Example 2
~~~~~~~~~
-Below is an example of a more complicated set of route statements you might
-add to your application:
+Below is an example of a more complicated set of route statements you might add
+to your application:
.. code-block:: python
:linenos:
@@ -587,33 +583,32 @@ forms:
and attached to the :term:`request` will consist of ``{'idea':'1'}``.
- When a URL matches the pattern ``/users/{user}``, the view callable
- available at the dotted Python pathname ``mypackage.views.user_view`` will
- be called. For the specific URL ``/users/1``, the ``matchdict`` generated
- and attached to the :term:`request` will consist of ``{'user':'1'}``.
+ available at the dotted Python pathname ``mypackage.views.user_view`` will be
+ called. For the specific URL ``/users/1``, the ``matchdict`` generated and
+ attached to the :term:`request` will consist of ``{'user':'1'}``.
- When a URL matches the pattern ``/tags/{tag}``, the view callable available
at the dotted Python pathname ``mypackage.views.tag_view`` will be called.
- For the specific URL ``/tags/1``, the ``matchdict`` generated and attached
- to the :term:`request` will consist of ``{'tag':'1'}``.
+ For the specific URL ``/tags/1``, the ``matchdict`` generated and attached to
+ the :term:`request` will consist of ``{'tag':'1'}``.
In this example we've again associated each of our routes with a :term:`view
-callable` directly. In all cases, the request, which will have a
-``matchdict`` attribute detailing the information found in the URL by the
-process will be passed to the view callable.
+callable` directly. In all cases, the request, which will have a ``matchdict``
+attribute detailing the information found in the URL by the process will be
+passed to the view callable.
Example 3
~~~~~~~~~
-The :term:`context` resource object passed in to a view found as the result
-of URL dispatch will, by default, be an instance of the object returned by
-the :term:`root factory` configured at startup time (the ``root_factory``
-argument to the :term:`Configurator` used to configure the application).
+The :term:`context` resource object passed in to a view found as the result of
+URL dispatch will, by default, be an instance of the object returned by the
+:term:`root factory` configured at startup time (the ``root_factory`` argument
+to the :term:`Configurator` used to configure the application).
You can override this behavior by passing in a ``factory`` argument to the
:meth:`~pyramid.config.Configurator.add_route` method for a particular route.
-The ``factory`` should be a callable that accepts a :term:`request` and
-returns an instance of a class that will be the context resource used by the
-view.
+The ``factory`` should be a callable that accepts a :term:`request` and returns
+an instance of a class that will be the context resource used by the view.
An example of using a route with a factory:
@@ -685,9 +680,9 @@ Or provide the literal string ``/`` as the pattern:
Generating Route URLs
---------------------
-Use the :meth:`pyramid.request.Request.route_url` method to generate URLs
-based on route patterns. For example, if you've configured a route with the
-``name`` "foo" and the ``pattern`` "{a}/{b}/{c}", you might do this.
+Use the :meth:`pyramid.request.Request.route_url` method to generate URLs based
+on route patterns. For example, if you've configured a route with the ``name``
+"foo" and the ``pattern`` "{a}/{b}/{c}", you might do this.
.. code-block:: python
:linenos:
@@ -707,13 +702,12 @@ To generate only the *path* portion of a URL from a route, use the
This will return the string ``/1/2/3`` rather than a full URL.
-Replacement values passed to ``route_url`` or ``route_path`` must be Unicode
-or bytestrings encoded in UTF-8. One exception to this rule exists: if
-you're trying to replace a "remainder" match value (a ``*stararg``
-replacement value), the value may be a tuple containing Unicode strings or
-UTF-8 strings.
+Replacement values passed to ``route_url`` or ``route_path`` must be Unicode or
+bytestrings encoded in UTF-8. One exception to this rule exists: if you're
+trying to replace a "remainder" match value (a ``*stararg`` replacement value),
+the value may be a tuple containing Unicode strings or UTF-8 strings.
-Note that URLs and paths generated by ``route_path`` and ``route_url`` are
+Note that URLs and paths generated by ``route_url`` and ``route_path`` are
always URL-quoted string types (they contain no non-ASCII characters).
Therefore, if you've added a route like so:
@@ -727,7 +721,7 @@ And you later generate a URL using ``route_path`` or ``route_url`` like so:
url = request.route_path('la', city=u'Québec')
-You will wind up with the path encoded to UTF-8 and URL quoted like so:
+You will wind up with the path encoded to UTF-8 and URL-quoted like so:
.. code-block:: text
@@ -759,7 +753,7 @@ You can get a similar result by passing a tuple composed of path elements:
url = request.route_path('abc', foo=(u'Québec', u'biz'))
-Each value in the tuple will be url-quoted and joined by slashes in this case:
+Each value in the tuple will be URL-quoted and joined by slashes in this case:
.. code-block:: text
@@ -793,7 +787,7 @@ 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`
+ the ``static`` argument to :meth:`~pyramid.config.Configurator.add_route`.
.. _external_route_narr:
@@ -836,13 +830,13 @@ argument to :meth:`pyramid.config.Configurator.add_notfound_view` or the
equivalent ``append_slash`` argument to the
:class:`pyramid.view.notfound_view_config` decorator.
-Adding ``append_slash=True`` is a way to automatically redirect requests
-where the URL lacks a trailing slash, but requires one to match the proper
-route. When configured, along with at least one other route in your
-application, this view will be invoked if the value of ``PATH_INFO`` does not
-already end in a slash, and if the value of ``PATH_INFO`` *plus* a slash
-matches any route's pattern. In this case it does an HTTP redirect to the
-slash-appended ``PATH_INFO``. In addition you may pass anything that implements
+Adding ``append_slash=True`` is a way to automatically redirect requests where
+the URL lacks a trailing slash, but requires one to match the proper route.
+When configured, along with at least one other route in your application, this
+view will be invoked if the value of ``PATH_INFO`` does not already end in a
+slash, and if the value of ``PATH_INFO`` *plus* a slash matches any route's
+pattern. In this case it does an HTTP redirect to the slash-appended
+``PATH_INFO``. In addition you may pass anything that implements
:class:`pyramid.interfaces.IResponse` which will then be used in place of the
default class (:class:`pyramid.httpexceptions.HTTPFound`).
@@ -872,12 +866,11 @@ application:
config.add_notfound_view(notfound, append_slash=True)
If a request enters the application with the ``PATH_INFO`` value of
-``/no_slash``, the first route will match and the browser will show "No
-slash". However, if a request enters the application with the ``PATH_INFO``
-value of ``/no_slash/``, *no* route will match, and the slash-appending not
-found view will not find a matching route with an appended slash. As a
-result, the ``notfound`` view will be called and it will return a "Not found,
-bro." body.
+``/no_slash``, the first route will match and the browser will show "No slash".
+However, if a request enters the application with the ``PATH_INFO`` value of
+``/no_slash/``, *no* route will match, and the slash-appending not found view
+will not find a matching route with an appended slash. As a result, the
+``notfound`` view will be called and it will return a "Not found, bro." body.
If a request enters the application with the ``PATH_INFO`` value of
``/has_slash/``, the second route will match. If a request enters the
@@ -934,10 +927,10 @@ Debugging Route Matching
It's useful to be able to take a peek under the hood when requests that enter
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
-:app:`Pyramid` application will be printed to the ``stderr`` of the console
+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 :app:`Pyramid` application will be printed to the ``stderr`` of the console
which you started the application from. For example:
.. code-block:: text
@@ -954,11 +947,11 @@ which you started the application from. For example:
http://localhost:6543/static/logo.png; \
route_name: 'static/', ....
-See :ref:`environment_chapter` for more information about how, and where to
-set these values.
+See :ref:`environment_chapter` for more information about how and where to set
+these values.
-You can also use the ``proutes`` command to see a display of all the
-routes configured in your application; for more information, see
+You can also use the ``proutes`` command to see a display of all the routes
+configured in your application. For more information, see
:ref:`displaying_application_routes`.
.. _route_prefix:
@@ -979,11 +972,11 @@ named ``route_prefix`` which can be useful to authors of URL-dispatch-based
applications. If ``route_prefix`` is supplied to the include method, it must
be a string. This string represents a route prefix that will be prepended to
all route patterns added by the *included* configuration. Any calls to
-:meth:`pyramid.config.Configurator.add_route` within the included callable
-will have their pattern prefixed with the value of ``route_prefix``. This can
-be used to help mount a set of routes at a different location than the
-included callable's author intended while still maintaining the same route
-names. For example:
+:meth:`pyramid.config.Configurator.add_route` within the included callable will
+have their pattern prefixed with the value of ``route_prefix``. This can be
+used to help mount a set of routes at a different location than the included
+callable's author intended while still maintaining the same route names. For
+example:
.. code-block:: python
:linenos:
@@ -998,15 +991,15 @@ names. For example:
config.include(users_include, route_prefix='/users')
In the above configuration, the ``show_users`` route will have an effective
-route pattern of ``/users/show``, instead of ``/show`` because the
+route pattern of ``/users/show`` instead of ``/show`` because the
``route_prefix`` argument will be prepended to the pattern. The route will
then only match if the URL path is ``/users/show``, and when the
:meth:`pyramid.request.Request.route_url` function is called with the route
name ``show_users``, it will generate a URL with that same path.
Route prefixes are recursive, so if a callable executed via an include itself
-turns around and includes another callable, the second-level route prefix
-will be prepended with the first:
+turns around and includes another callable, the second-level route prefix will
+be prepended with the first:
.. code-block:: python
:linenos:
@@ -1025,15 +1018,15 @@ will be prepended with the first:
config.include(users_include, route_prefix='/users')
In the above configuration, the ``show_users`` route will still have an
-effective route pattern of ``/users/show``. The ``show_times`` route
-however, will have an effective pattern of ``/users/timing/times``.
+effective route pattern of ``/users/show``. The ``show_times`` route, however,
+will have an effective pattern of ``/users/timing/times``.
-Route prefixes have no impact on the requirement that the set of route
-*names* in any given Pyramid configuration must be entirely unique. If you
-compose your URL dispatch application out of many small subapplications using
-:meth:`pyramid.config.Configurator.include`, it's wise to use a dotted name
-for your route names, so they'll be unlikely to conflict with other packages
-that may be added in the future. For example:
+Route prefixes have no impact on the requirement that the set of route *names*
+in any given Pyramid configuration must be entirely unique. If you compose
+your URL dispatch application out of many small subapplications using
+:meth:`pyramid.config.Configurator.include`, it's wise to use a dotted name for
+your route names so they'll be unlikely to conflict with other packages that
+may be added in the future. For example:
.. code-block:: python
:linenos:
@@ -1060,15 +1053,16 @@ Custom Route Predicates
-----------------------
Each of the predicate callables fed to the ``custom_predicates`` argument of
-:meth:`~pyramid.config.Configurator.add_route` must be a callable accepting
-two arguments. The first argument passed to a custom predicate is a
-dictionary conventionally named ``info``. The second argument is the current
+:meth:`~pyramid.config.Configurator.add_route` must be a callable accepting two
+arguments. The first argument passed to a custom predicate is a dictionary
+conventionally named ``info``. The second argument is the current
:term:`request` object.
-The ``info`` dictionary has a number of contained values: ``match`` is a
-dictionary: it represents the arguments matched in the URL by the route.
-``route`` is an object representing the route which was matched (see
-:class:`pyramid.interfaces.IRoute` for the API of such a route object).
+The ``info`` dictionary has a number of contained values, including ``match``
+and ``route``. ``match`` is a dictionary which represents the arguments matched
+in the URL by the route. ``route`` is an object representing the route which
+was matched (see :class:`pyramid.interfaces.IRoute` for the API of such a route
+object).
``info['match']`` is useful when predicates need access to the route match.
For example:
@@ -1118,9 +1112,9 @@ instance, a predicate might do some type conversion of values:
config.add_route('ymd', '/{year}/{month}/{day}',
custom_predicates=(ymd_to_int,))
-Note that a conversion predicate is still a predicate so it must return
-``True`` or ``False``; a predicate that does *only* conversion, such as the
-one we demonstrate above should unconditionally return ``True``.
+Note that a conversion predicate is still a predicate, so it must return
+``True`` or ``False``. A predicate that does *only* conversion, such as the one
+we demonstrate above, should unconditionally return ``True``.
To avoid the try/except uncertainty, the route pattern can contain regular
expressions specifying requirements for that marker. For instance:
@@ -1141,31 +1135,31 @@ expressions specifying requirements for that marker. For instance:
config.add_route('ymd', '/{year:\d+}/{month:\d+}/{day:\d+}',
custom_predicates=(ymd_to_int,))
-Now the try/except is no longer needed because the route will not match at
-all unless these markers match ``\d+`` which requires them to be valid digits
-for an ``int`` type conversion.
+Now the try/except is no longer needed because the route will not match at all
+unless these markers match ``\d+`` which requires them to be valid digits for
+an ``int`` type conversion.
-The ``match`` dictionary passed within ``info`` to each predicate attached to
-a route will be the same dictionary. Therefore, when registering a custom
-predicate which modifies the ``match`` dict, the code registering the
-predicate should usually arrange for the predicate to be the *last* custom
-predicate in the custom predicate list. Otherwise, custom predicates which
-fire subsequent to the predicate which performs the ``match`` modification
-will receive the *modified* match dictionary.
+The ``match`` dictionary passed within ``info`` to each predicate attached to a
+route will be the same dictionary. Therefore, when registering a custom
+predicate which modifies the ``match`` dict, the code registering the predicate
+should usually arrange for the predicate to be the *last* custom predicate in
+the custom predicate list. Otherwise, custom predicates which fire subsequent
+to the predicate which performs the ``match`` modification will receive the
+*modified* match dictionary.
.. warning::
It is a poor idea to rely on ordering of custom predicates to build a
conversion pipeline, where one predicate depends on the side effect of
- another. For instance, it's a poor idea to register two custom
- predicates, one which handles conversion of a value to an int, the next
- which handles conversion of that integer to some custom object. Just do
- all that in a single custom predicate.
+ another. For instance, it's a poor idea to register two custom predicates,
+ one which handles conversion of a value to an int, the next which handles
+ conversion of that integer to some custom object. Just do all that in a
+ single custom predicate.
The ``route`` object in the ``info`` dict is an object that has two useful
-attributes: ``name`` and ``pattern``. The ``name`` attribute is the route
-name. The ``pattern`` attribute is the route pattern. An example of using
-the route in a set of route predicates:
+attributes: ``name`` and ``pattern``. The ``name`` attribute is the route name.
+The ``pattern`` attribute is the route pattern. Here's an example of using the
+route in a set of route predicates:
.. code-block:: python
:linenos:
@@ -1180,14 +1174,14 @@ the route in a set of route predicates:
custom_predicates=(twenty_ten,))
The above predicate, when added to a number of route configurations ensures
-that the year match argument is '2010' if and only if the route name is
-'ymd', 'ym', or 'y'.
+that the year match argument is '2010' if and only if the route name is 'ymd',
+'ym', or 'y'.
-You can also caption the predicates by setting the ``__text__``
-attribute. This will help you with the ``pviews`` command (see
+You can also caption the predicates by setting the ``__text__`` attribute. This
+will help you with the ``pviews`` command (see
:ref:`displaying_application_routes`) and the ``pyramid_debugtoolbar``.
-If a predicate is a class just add __text__ property in a standard manner.
+If a predicate is a class, just add ``__text__`` property in a standard manner.
.. code-block:: python
:linenos:
@@ -1199,8 +1193,8 @@ If a predicate is a class just add __text__ property in a standard manner.
class DummyCustomPredicate2(object):
__text__ = 'my custom class predicate'
-If a predicate is a method you'll need to assign it after method declaration
-(see `PEP 232 <http://www.python.org/dev/peps/pep-0232/>`_)
+If a predicate is a method, you'll need to assign it after method declaration
+(see `PEP 232 <http://www.python.org/dev/peps/pep-0232/>`_).
.. code-block:: python
:linenos:
@@ -1209,8 +1203,8 @@ If a predicate is a method you'll need to assign it after method declaration
pass
custom_predicate.__text__ = 'my custom method predicate'
-If a predicate is a classmethod using @classmethod will not work, but you can
-still easily do it by wrapping it in classmethod call.
+If a predicate is a classmethod, using ``@classmethod`` will not work, but you
+can still easily do it by wrapping it in a classmethod call.
.. code-block:: python
:linenos:
@@ -1220,7 +1214,7 @@ still easily do it by wrapping it in classmethod call.
classmethod_predicate.__text__ = 'my classmethod predicate'
classmethod_predicate = classmethod(classmethod_predicate)
-Same will work with staticmethod, just use ``staticmethod`` instead of
+The same will work with ``staticmethod``, using ``staticmethod`` instead of
``classmethod``.
.. seealso::
@@ -1236,10 +1230,10 @@ Same will work with staticmethod, just use ``staticmethod`` instead of
Route Factories
---------------
-Although it is not a particular common need in basic applications, a "route"
-configuration declaration can mention a "factory". When that route matches a
-request, and a factory is attached to a route, the :term:`root factory`
-passed at startup time to the :term:`Configurator` is ignored; instead the
+Although it is not a particularly common need in basic applications, a "route"
+configuration declaration can mention a "factory". When a route matches a
+request, and a factory is attached to the route, the :term:`root factory`
+passed at startup time to the :term:`Configurator` is ignored. Instead the
factory associated with the route is used to generate a :term:`root` object.
This object will usually be used as the :term:`context` resource of the view
callable ultimately found via :term:`view lookup`.
@@ -1255,8 +1249,8 @@ The factory can either be a Python object or a :term:`dotted Python name` (a
string) which points to such a Python object, as it is above.
In this way, each route can use a different factory, making it possible to
-supply a different :term:`context` resource object to the view related to
-each particular route.
+supply a different :term:`context` resource object to the view related to each
+particular route.
A factory must be a callable which accepts a request and returns an arbitrary
Python object. For example, the below class can be used as a factory:
@@ -1268,33 +1262,31 @@ Python object. For example, the below class can be used as a factory:
def __init__(self, request):
pass
-A route factory is actually conceptually identical to the :term:`root
-factory` described at :ref:`the_resource_tree`.
+A route factory is actually conceptually identical to the :term:`root factory`
+described at :ref:`the_resource_tree`.
Supplying a different resource factory for each route is useful when you're
trying to use a :app:`Pyramid` :term:`authorization policy` to provide
-declarative, "context sensitive" security checks; each resource can maintain
-a separate :term:`ACL`, as documented in
-:ref:`using_security_with_urldispatch`. It is also useful when you wish to
-combine URL dispatch with :term:`traversal` as documented within
-:ref:`hybrid_chapter`.
+declarative, "context sensitive" security checks. Each resource can maintain a
+separate :term:`ACL`, as documented in :ref:`using_security_with_urldispatch`.
+It is also useful when you wish to combine URL dispatch with :term:`traversal`
+as documented within :ref:`hybrid_chapter`.
.. index::
pair: URL dispatch; security
.. _using_security_with_urldispatch:
-Using :app:`Pyramid` Security With URL Dispatch
---------------------------------------------------
+Using :app:`Pyramid` Security with URL Dispatch
+-----------------------------------------------
:app:`Pyramid` provides its own security framework which consults an
-:term:`authorization policy` before allowing any application code to be
-called. This framework operates in terms of an access control list, which is
-stored as an ``__acl__`` attribute of a resource object. A common thing to
-want to do is to attach an ``__acl__`` to the resource object dynamically for
-declarative security purposes. You can use the ``factory`` argument that
-points at a factory which attaches a custom ``__acl__`` to an object at its
-creation time.
+:term:`authorization policy` before allowing any application code to be called.
+This framework operates in terms of an access control list, which is stored as
+an ``__acl__`` attribute of a resource object. A common thing to want to do is
+to attach an ``__acl__`` to the resource object dynamically for declarative
+security purposes. You can use the ``factory`` argument that points at a
+factory which attaches a custom ``__acl__`` to an object at its creation time.
Such a ``factory`` might look like so:
@@ -1310,15 +1302,15 @@ Such a ``factory`` might look like so:
If the route ``archives/{article}`` is matched, and the article number is
``1``, :app:`Pyramid` will generate an ``Article`` :term:`context` resource
-with an ACL on it that allows the ``editor`` principal the ``view``
-permission. Obviously you can do more generic things than inspect the routes
-match dict to see if the ``article`` argument matches a particular string;
-our sample ``Article`` factory class is not very ambitious.
+with an ACL on it that allows the ``editor`` principal the ``view`` permission.
+Obviously you can do more generic things than inspect the route's match dict to
+see if the ``article`` argument matches a particular string. Our sample
+``Article`` factory class is not very ambitious.
.. note::
- See :ref:`security_chapter` for more information about
- :app:`Pyramid` security and ACLs.
+ See :ref:`security_chapter` for more information about :app:`Pyramid`
+ security and ACLs.
.. index::
pair: route; view callable lookup details
@@ -1330,10 +1322,9 @@ When a request enters the system which matches the pattern of the route, the
usual result is simple: the view callable associated with the route is
invoked with the request that caused the invocation.
-For most usage, you needn't understand more than this; how it works is an
-implementation detail. In the interest of completeness, however, we'll
-explain how it *does* work in this section. You can skip it if you're
-uninterested.
+For most usage, you needn't understand more than this. How it works is an
+implementation detail. In the interest of completeness, however, we'll explain
+how it *does* work in this section. You can skip it if you're uninterested.
When a view is associated with a route configuration, :app:`Pyramid` ensures
that a :term:`view configuration` is registered that will always be found
@@ -1349,26 +1340,25 @@ when the route pattern is matched during a request. To do so:
- At runtime, when a request causes any route to match, the :term:`request`
object is decorated with the route-specific interface.
-- The fact that the request is decorated with a route-specific interface
- causes the :term:`view lookup` machinery to always use the view callable
- registered using that interface by the route configuration to service
- requests that match the route pattern.
+- The fact that the request is decorated with a route-specific interface causes
+ the :term:`view lookup` machinery to always use the view callable registered
+ using that interface by the route configuration to service requests that
+ match the route pattern.
As we can see from the above description, technically, URL dispatch doesn't
-actually map a URL pattern directly to a view callable. Instead, URL
-dispatch is a :term:`resource location` mechanism. A :app:`Pyramid`
-:term:`resource location` subsystem (i.e., :term:`URL dispatch` or
-:term:`traversal`) finds a :term:`resource` object that is the
-:term:`context` of a :term:`request`. Once the :term:`context` is determined,
-a separate subsystem named :term:`view lookup` is then responsible for
-finding and invoking a :term:`view callable` based on information available
-in the context and the request. When URL dispatch is used, the resource
-location and view lookup subsystems provided by :app:`Pyramid` are still
-being utilized, but in a way which does not require a developer to understand
-either of them in detail.
-
-If no route is matched using :term:`URL dispatch`, :app:`Pyramid` falls back
-to :term:`traversal` to handle the :term:`request`.
+actually map a URL pattern directly to a view callable. Instead URL dispatch
+is a :term:`resource location` mechanism. A :app:`Pyramid` :term:`resource
+location` subsystem (i.e., :term:`URL dispatch` or :term:`traversal`) finds a
+:term:`resource` object that is the :term:`context` of a :term:`request`. Once
+the :term:`context` is determined, a separate subsystem named :term:`view
+lookup` is then responsible for finding and invoking a :term:`view callable`
+based on information available in the context and the request. When URL
+dispatch is used, the resource location and view lookup subsystems provided by
+:app:`Pyramid` are still being utilized, but in a way which does not require a
+developer to understand either of them in detail.
+
+If no route is matched using :term:`URL dispatch`, :app:`Pyramid` falls back to
+:term:`traversal` to handle the :term:`request`.
References
----------
diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst
index 53f6888b3..0edf03353 100644
--- a/docs/narr/vhosting.rst
+++ b/docs/narr/vhosting.rst
@@ -6,13 +6,13 @@
Virtual Hosting
===============
-"Virtual hosting" is, loosely, the act of serving a :app:`Pyramid`
-application or a portion of a :app:`Pyramid` application under a
-URL space that it does not "naturally" inhabit.
+"Virtual hosting" is, loosely, the act of serving a :app:`Pyramid` application
+or a portion of a :app:`Pyramid` application under a URL space that it does not
+"naturally" inhabit.
-:app:`Pyramid` provides facilities for serving an application under
-a URL "prefix", as well as serving a *portion* of a :term:`traversal`
-based application under a root URL.
+:app:`Pyramid` provides facilities for serving an application under a URL
+"prefix", as well as serving a *portion* of a :term:`traversal` based
+application under a root URL.
.. index::
single: hosting an app under a prefix
@@ -20,27 +20,26 @@ based application under a root URL.
Hosting an Application Under a URL Prefix
-----------------------------------------
-:app:`Pyramid` supports a common form of virtual hosting whereby you
-can host a :app:`Pyramid` application as a "subset" of some other site
-(e.g. under ``http://example.com/mypyramidapplication/`` as opposed to
-under ``http://example.com/``).
+:app:`Pyramid` supports a common form of virtual hosting whereby you can host a
+:app:`Pyramid` application as a "subset" of some other site (e.g., under
+``http://example.com/mypyramidapplication/`` as opposed to under
+``http://example.com/``).
If you use a "pure Python" environment, this functionality can be provided by
-Paste's `urlmap <http://pythonpaste.org/modules/urlmap.html>`_ "composite"
-WSGI application. Alternately, you can use :term:`mod_wsgi` to serve your
-application, which handles this virtual hosting translation for you "under
-the hood".
-
-If you use the ``urlmap`` composite application "in front" of a
-:app:`Pyramid` application or if you use :term:`mod_wsgi` to serve
-up a :app:`Pyramid` application, nothing special needs to be done
-within the application for URLs to be generated that contain a
-prefix. :mod:`paste.urlmap` and :term:`mod_wsgi` manipulate the
-:term:`WSGI` environment in such a way that the ``PATH_INFO`` and
-``SCRIPT_NAME`` variables are correct for some given prefix.
-
-Here's an example of a PasteDeploy configuration snippet that includes
-a ``urlmap`` composite.
+Paste's `urlmap <http://pythonpaste.org/modules/urlmap.html>`_ "composite" WSGI
+application. Alternatively, you can use :term:`mod_wsgi` to serve your
+application, which handles this virtual hosting translation for you "under the
+hood".
+
+If you use the ``urlmap`` composite application "in front" of a :app:`Pyramid`
+application or if you use :term:`mod_wsgi` to serve up a :app:`Pyramid`
+application, nothing special needs to be done within the application for URLs
+to be generated that contain a prefix. :mod:`paste.urlmap` and :term:`mod_wsgi`
+manipulate the :term:`WSGI` environment in such a way that the ``PATH_INFO``
+and ``SCRIPT_NAME`` variables are correct for some given prefix.
+
+Here's an example of a PasteDeploy configuration snippet that includes a
+``urlmap`` composite.
.. code-block:: ini
:linenos:
@@ -52,23 +51,21 @@ a ``urlmap`` composite.
use = egg:Paste#urlmap
/pyramidapp = mypyramidapp
-This "roots" the :app:`Pyramid` application at the prefix
-``/pyramidapp`` and serves up the composite as the "main" application
-in the file.
+This "roots" the :app:`Pyramid` application at the prefix ``/pyramidapp`` and
+serves up the composite as the "main" application in the file.
-.. note:: If you're using an Apache server to proxy to a Paste
- ``urlmap`` composite, you may have to use the `ProxyPreserveHost
+.. note:: If you're using an Apache server to proxy to a Paste ``urlmap``
+ composite, you may have to use the `ProxyPreserveHost
<http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypreservehost>`_
directive to pass the original ``HTTP_HOST`` header along to the
- application, so URLs get generated properly. As of this writing
- the ``urlmap`` composite does not seem to respect the
- ``HTTP_X_FORWARDED_HOST`` parameter, which will contain the
- original host header even if ``HTTP_HOST`` is incorrect.
+ application, so URLs get generated properly. As of this writing the
+ ``urlmap`` composite does not seem to respect the ``HTTP_X_FORWARDED_HOST``
+ parameter, which will contain the original host header even if ``HTTP_HOST``
+ is incorrect.
-If you use :term:`mod_wsgi`, you do not need to use a ``composite``
-application in your ``.ini`` file. The ``WSGIScriptAlias``
-configuration setting in a :term:`mod_wsgi` configuration does the
-work for you:
+If you use :term:`mod_wsgi`, you do not need to use a ``composite`` application
+in your ``.ini`` file. The ``WSGIScriptAlias`` configuration setting in a
+:term:`mod_wsgi` configuration does the work for you:
.. code-block:: apache
:linenos:
@@ -87,8 +84,7 @@ Virtual Root Support
--------------------
:app:`Pyramid` also supports "virtual roots", which can be used in
-:term:`traversal` -based (but not :term:`URL dispatch` -based)
-applications.
+:term:`traversal`-based (but not :term:`URL dispatch`-based) applications.
Virtual root support is useful when you'd like to host some resource in a
:app:`Pyramid` resource tree as an application under a URL pathname that does
@@ -98,15 +94,14 @@ object at the traversal path ``/cms`` as an application reachable via
To specify a virtual root, cause an environment variable to be inserted into
the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value that is the absolute
-pathname to the resource object in the resource tree that should behave as
-the "root" resource. As a result, the traversal machinery will respect this
-value during traversal (prepending it to the PATH_INFO before traversal
-starts), and the :meth:`pyramid.request.Request.resource_url` API will
-generate the "correct" virtually-rooted URLs.
+pathname to the resource object in the resource tree that should behave as the
+"root" resource. As a result, the traversal machinery will respect this value
+during traversal (prepending it to the PATH_INFO before traversal starts), and
+the :meth:`pyramid.request.Request.resource_url` API will generate the
+"correct" virtually-rooted URLs.
-An example of an Apache ``mod_proxy`` configuration that will host the
-``/cms`` subobject as ``http://www.example.com/`` using this facility
-is below:
+An example of an Apache ``mod_proxy`` configuration that will host the ``/cms``
+subobject as ``http://www.example.com/`` using this facility is below:
.. code-block:: apache
:linenos:
@@ -121,13 +116,12 @@ is below:
RequestHeader add X-Vhm-Root /cms
</VirtualHost>
-.. note:: Use of the ``RequestHeader`` directive requires that the
- Apache `mod_headers
- <http://httpd.apache.org/docs/2.2/mod/mod_headers.html>`_ module be
- available in the Apache environment you're using.
+.. note:: Use of the ``RequestHeader`` directive requires that the Apache
+ `mod_headers <http://httpd.apache.org/docs/2.2/mod/mod_headers.html>`_
+ module be available in the Apache environment you're using.
-For a :app:`Pyramid` application running under :term:`mod_wsgi`,
-the same can be achieved using ``SetEnv``:
+For a :app:`Pyramid` application running under :term:`mod_wsgi`, the same can
+be achieved using ``SetEnv``:
.. code-block:: apache
:linenos:
@@ -136,17 +130,16 @@ the same can be achieved using ``SetEnv``:
SetEnv HTTP_X_VHM_ROOT /cms
</Location>
-Setting a virtual root has no effect when using an application based
-on :term:`URL dispatch`.
+Setting a virtual root has no effect when using an application based on
+:term:`URL dispatch`.
Further Documentation and Examples
----------------------------------
The API documentation in :ref:`traversal_module` documents a
-:func:`pyramid.traversal.virtual_root` API. When called, it
-returns the virtual root object (or the physical root object if no
-virtual root has been specified).
-
-:ref:`modwsgi_tutorial` has detailed information about using
-:term:`mod_wsgi` to serve :app:`Pyramid` applications.
+:func:`pyramid.traversal.virtual_root` API. When called, it returns the
+virtual root object (or the physical root object if no virtual root has been
+specified).
+:ref:`modwsgi_tutorial` has detailed information about using :term:`mod_wsgi`
+to serve :app:`Pyramid` applications.
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index d5203c6ba..0bd52b6e2 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -10,8 +10,8 @@ View Configuration
.. index::
single: view lookup
-:term:`View lookup` is the :app:`Pyramid` subsystem responsible for finding
-and invoking a :term:`view callable`. :term:`View configuration` controls how
+:term:`View lookup` is the :app:`Pyramid` subsystem responsible for finding and
+invoking a :term:`view callable`. :term:`View configuration` controls how
:term:`view lookup` operates in your application. During any given request,
view configuration information is compared against request data by the view
lookup subsystem in order to find the "best" view callable for that request.
@@ -30,8 +30,8 @@ Mapping a Resource or URL Pattern to a View Callable
A developer makes a :term:`view callable` available for use within a
:app:`Pyramid` application via :term:`view configuration`. A view
configuration associates a view callable with a set of statements that
-determine the set of circumstances which must be true for the view callable
-to be invoked.
+determine the set of circumstances which must be true for the view callable to
+be invoked.
A view configuration statement is made about information present in the
:term:`context` resource and the :term:`request`.
@@ -56,35 +56,34 @@ View Configuration Parameters
All forms of view configuration accept the same general types of arguments.
Many arguments supplied during view configuration are :term:`view predicate`
-arguments. View predicate arguments used during view configuration are used
-to narrow the set of circumstances in which :term:`view lookup` will find a
+arguments. View predicate arguments used during view configuration are used to
+narrow the set of circumstances in which :term:`view lookup` will find a
particular view callable.
:term:`View predicate` attributes are an important part of view configuration
that enables the :term:`view lookup` subsystem to find and invoke the
-appropriate view. The greater the number of predicate attributes possessed by a
-view's configuration, the more specific the circumstances need to be before
-the registered view callable will be invoked. The fewer the number of predicates
-which are supplied to a particular view configuration, the more likely it is
-that the associated view callable will be invoked. A view with five
-predicates will always be found and evaluated before a view with two, for
+appropriate view. The greater the number of predicate attributes possessed by
+a view's configuration, the more specific the circumstances need to be before
+the registered view callable will be invoked. The fewer the number of
+predicates which are supplied to a particular view configuration, the more
+likely it is that the associated view callable will be invoked. A view with
+five predicates will always be found and evaluated before a view with two, for
example.
-This does not mean however, that :app:`Pyramid` "stops looking" when it
-finds a view registration with predicates that don't match. If one set
-of view predicates does not match, the "next most specific" view (if
-any) is consulted for predicates, and so on, until a view is found, or
-no view can be matched up with the request. The first view with a set
-of predicates all of which match the request environment will be
-invoked.
+This does not mean however, that :app:`Pyramid` "stops looking" when it finds a
+view registration with predicates that don't match. If one set of view
+predicates does not match, the "next most specific" view (if any) is consulted
+for predicates, and so on, until a view is found, or no view can be matched up
+with the request. The first view with a set of predicates all of which match
+the request environment will be invoked.
If no view can be found with predicates which allow it to be matched up with
the request, :app:`Pyramid` will return an error to the user's browser,
representing a "not found" (404) page. See :ref:`changing_the_notfound_view`
for more information about changing the default :term:`Not Found View`.
-Other view configuration arguments are non-predicate arguments. These tend
-to modify the response of the view callable or prevent the view callable from
+Other view configuration arguments are non-predicate arguments. These tend to
+modify the response of the view callable or prevent the view callable from
being invoked due to an authorization policy. The presence of non-predicate
arguments in a view configuration does not narrow the circumstances in which
the view callable will be invoked.
@@ -96,56 +95,55 @@ Non-Predicate Arguments
``permission``
The name of a :term:`permission` that the user must possess in order to
- invoke the :term:`view callable`. See :ref:`view_security_section` for
- more information about view security and permissions.
+ invoke the :term:`view callable`. See :ref:`view_security_section` for more
+ information about view security and permissions.
- If ``permission`` is not supplied, no permission is registered for this
- view (it's accessible by any caller).
+ If ``permission`` is not supplied, no permission is registered for this view
+ (it's accessible by any caller).
``attr``
The view machinery defaults to using the ``__call__`` method of the
:term:`view callable` (or the function itself, if the view callable is a
function) to obtain a response. The ``attr`` value allows you to vary the
- method attribute used to obtain the response. For example, if your view
- was a class, and the class has a method named ``index`` and you wanted to
- use this method instead of the class' ``__call__`` method to return the
- response, you'd say ``attr="index"`` in the view configuration for the
- view. This is most useful when the view definition is a class.
+ method attribute used to obtain the response. For example, if your view was
+ a class, and the class has a method named ``index`` and you wanted to use
+ this method instead of the class's ``__call__`` method to return the
+ response, you'd say ``attr="index"`` in the view configuration for the view.
+ This is most useful when the view definition is a class.
If ``attr`` is not supplied, ``None`` is used (implying the function itself
- if the view is a function, or the ``__call__`` callable attribute if the
- view is a class).
+ if the view is a function, or the ``__call__`` callable attribute if the view
+ is a class).
``renderer``
- Denotes the :term:`renderer` implementation which will be used to construct
- a :term:`response` from the associated view callable's return value.
-
+ Denotes the :term:`renderer` implementation which will be used to construct a
+ :term:`response` from the associated view callable's return value.
+
.. seealso:: See also :ref:`renderers_chapter`.
- This is either a single string term (e.g. ``json``) or a string implying a
- path or :term:`asset specification` (e.g. ``templates/views.pt``) naming a
- :term:`renderer` implementation. If the ``renderer`` value does not
- contain a dot (``.``), the specified string will be used to look up a
- renderer implementation, and that renderer implementation will be used to
- construct a response from the view return value. If the ``renderer`` value
- contains a dot (``.``), the specified term will be treated as a path, and
- the filename extension of the last element in the path will be used to look
- up the renderer implementation, which will be passed the full path.
-
- When the renderer is a path, although a path is usually just a simple
- relative pathname (e.g. ``templates/foo.pt``, implying that a template
- named "foo.pt" is in the "templates" directory relative to the directory of
- the current :term:`package`), a path can be absolute, starting with a slash
- on UNIX or a drive letter prefix on Windows. The path can alternately be a
- :term:`asset specification` in the form
- ``some.dotted.package_name:relative/path``, making it possible to address
- template assets which live in a separate package.
+ This is either a single string term (e.g., ``json``) or a string implying a
+ path or :term:`asset specification` (e.g., ``templates/views.pt``) naming a
+ :term:`renderer` implementation. If the ``renderer`` value does not contain
+ a dot (``.``), the specified string will be used to look up a renderer
+ implementation, and that renderer implementation will be used to construct a
+ response from the view return value. If the ``renderer`` value contains a
+ dot (``.``), the specified term will be treated as a path, and the filename
+ extension of the last element in the path will be used to look up the
+ renderer implementation, which will be passed the full path.
+
+ When the renderer is a path—although a path is usually just a simple relative
+ pathname (e.g., ``templates/foo.pt``, implying that a template named "foo.pt"
+ is in the "templates" directory relative to the directory of the current
+ :term:`package`)—the path can be absolute, starting with a slash on UNIX or a
+ drive letter prefix on Windows. The path can alternatively be a :term:`asset
+ specification` in the form ``some.dotted.package_name:relative/path``, making
+ it possible to address template assets which live in a separate package.
The ``renderer`` attribute is optional. If it is not defined, the "null"
renderer is assumed (no rendering is performed and the value is passed back
- to the upstream :app:`Pyramid` machinery unchanged). Note that if the
- view callable itself returns a :term:`response` (see :ref:`the_response`),
- the specified renderer implementation is never called.
+ to the upstream :app:`Pyramid` machinery unchanged). Note that if the view
+ callable itself returns a :term:`response` (see :ref:`the_response`), the
+ specified renderer implementation is never called.
``http_cache``
When you supply an ``http_cache`` value to a view configuration, the
@@ -153,38 +151,36 @@ Non-Predicate Arguments
associated view callable are modified. The value for ``http_cache`` may be
one of the following:
- - A nonzero integer. If it's a nonzero integer, it's treated as a number
- of seconds. This number of seconds will be used to compute the
- ``Expires`` header and the ``Cache-Control: max-age`` parameter of
- responses to requests which call this view. For example:
- ``http_cache=3600`` instructs the requesting browser to 'cache this
- response for an hour, please'.
+ - A nonzero integer. If it's a nonzero integer, it's treated as a number of
+ seconds. This number of seconds will be used to compute the ``Expires``
+ header and the ``Cache-Control: max-age`` parameter of responses to
+ requests which call this view. For example: ``http_cache=3600`` instructs
+ the requesting browser to 'cache this response for an hour, please'.
- A ``datetime.timedelta`` instance. If it's a ``datetime.timedelta``
- instance, it will be converted into a number of seconds, and that number
- of seconds will be used to compute the ``Expires`` header and the
+ instance, it will be converted into a number of seconds, and that number of
+ seconds will be used to compute the ``Expires`` header and the
``Cache-Control: max-age`` parameter of responses to requests which call
this view. For example: ``http_cache=datetime.timedelta(days=1)``
instructs the requesting browser to 'cache this response for a day,
please'.
- - Zero (``0``). If the value is zero, the ``Cache-Control`` and
- ``Expires`` headers present in all responses from this view will be
- composed such that client browser cache (and any intermediate caches) are
- instructed to never cache the response.
-
- - A two-tuple. If it's a two tuple (e.g. ``http_cache=(1,
- {'public':True})``), the first value in the tuple may be a nonzero
- integer or a ``datetime.timedelta`` instance; in either case this value
- will be used as the number of seconds to cache the response. The second
- value in the tuple must be a dictionary. The values present in the
- dictionary will be used as input to the ``Cache-Control`` response
- header. For example: ``http_cache=(3600, {'public':True})`` means 'cache
- for an hour, and add ``public`` to the Cache-Control header of the
- response'. All keys and values supported by the
- ``webob.cachecontrol.CacheControl`` interface may be added to the
- dictionary. Supplying ``{'public':True}`` is equivalent to calling
- ``response.cache_control.public = True``.
+ - Zero (``0``). If the value is zero, the ``Cache-Control`` and ``Expires``
+ headers present in all responses from this view will be composed such that
+ client browser cache (and any intermediate caches) are instructed to never
+ cache the response.
+
+ - A two-tuple. If it's a two-tuple (e.g., ``http_cache=(1,
+ {'public':True})``), the first value in the tuple may be a nonzero integer
+ or a ``datetime.timedelta`` instance. In either case this value will be
+ used as the number of seconds to cache the response. The second value in
+ the tuple must be a dictionary. The values present in the dictionary will
+ be used as input to the ``Cache-Control`` response header. For example:
+ ``http_cache=(3600, {'public':True})`` means 'cache for an hour, and add
+ ``public`` to the Cache-Control header of the response'. All keys and
+ values supported by the ``webob.cachecontrol.CacheControl`` interface may
+ be added to the dictionary. Supplying ``{'public':True}`` is equivalent to
+ calling ``response.cache_control.public = True``.
Providing a non-tuple value as ``http_cache`` is equivalent to calling
``response.cache_expires(value)`` within your view's body.
@@ -192,9 +188,9 @@ Non-Predicate Arguments
Providing a two-tuple value as ``http_cache`` is equivalent to calling
``response.cache_expires(value[0], **value[1])`` within your view's body.
- If you wish to avoid influencing, the ``Expires`` header, and instead wish
- to only influence ``Cache-Control`` headers, pass a tuple as ``http_cache``
- with the first element of ``None``, e.g.: ``(None, {'public':True})``.
+ If you wish to avoid influencing the ``Expires`` header, and instead wish to
+ only influence ``Cache-Control`` headers, pass a tuple as ``http_cache`` with
+ the first element of ``None``, i.e., ``(None, {'public':True})``.
``wrapper``
The :term:`view name` of a different :term:`view configuration` which will
@@ -203,24 +199,24 @@ Non-Predicate Arguments
this view as the ``request.wrapped_response`` attribute of its own request.
Using a wrapper makes it possible to "chain" views together to form a
composite response. The response of the outermost wrapper view will be
- returned to the user. The wrapper view will be found as any view is found:
- see :ref:`view_lookup`. The "best" wrapper view will be found based on the
- lookup ordering: "under the hood" this wrapper view is looked up via
+ returned to the user. The wrapper view will be found as any view is found.
+ See :ref:`view_lookup`. The "best" wrapper view will be found based on the
+ lookup ordering. "Under the hood" this wrapper view is looked up via
``pyramid.view.render_view_to_response(context, request,
- 'wrapper_viewname')``. The context and request of a wrapper view is the
- same context and request of the inner view.
+ 'wrapper_viewname')``. The context and request of a wrapper view is the same
+ context and request of the inner view.
If ``wrapper`` is not supplied, no wrapper view is used.
``decorator``
A :term:`dotted Python name` to a function (or the function itself) which
- will be used to decorate the registered :term:`view callable`. The
- decorator function will be called with the view callable as a single
- argument. The view callable it is passed will accept ``(context,
- request)``. The decorator must return a replacement view callable which
- also accepts ``(context, request)``. The ``decorator`` may also be an
- iterable of decorators, in which case they will be applied one after the
- other to the view, in reverse order. For example::
+ will be used to decorate the registered :term:`view callable`. The decorator
+ function will be called with the view callable as a single argument. The
+ view callable it is passed will accept ``(context, request)``. The decorator
+ must return a replacement view callable which also accepts ``(context,
+ request)``. The ``decorator`` may also be an iterable of decorators, in which
+ case they will be applied one after the other to the view, in reverse order.
+ For example::
@view_config(..., decorator=(decorator2, decorator1))
def myview(request):
@@ -234,37 +230,51 @@ Non-Predicate Arguments
def myview(request):
...
+ All view callables in the decorator chain must return a response object
+ implementing :class:`pyramid.interfaces.IResponse` or raise an exception:
+
+ .. code-block:: python
+
+ def log_timer(wrapped):
+ def wrapper(context, request):
+ start = time.time()
+ response = wrapped(context, request)
+ duration = time.time() - start
+ response.headers['X-View-Time'] = '%.3f' % (duration,)
+ log.info('view took %.3f seconds', duration)
+ return response
+ return wrapper
+
``mapper``
A Python object or :term:`dotted Python name` which refers to a :term:`view
mapper`, or ``None``. By default it is ``None``, which indicates that the
view should use the default view mapper. This plug-point is useful for
- Pyramid extension developers, but it's not very useful for 'civilians' who
+ Pyramid extension developers, but it's not very useful for "civilians" who
are just developing stock Pyramid applications. Pay no attention to the man
behind the curtain.
Predicate Arguments
+++++++++++++++++++
-These arguments modify view lookup behavior. In general, the more predicate
-arguments that are supplied, the more specific, and narrower the usage of the
+These arguments modify view lookup behavior. In general the more predicate
+arguments that are supplied, the more specific and narrower the usage of the
configured view.
``name``
The :term:`view name` required to match this view callable. A ``name``
- argument is typically only used when your application uses
- :term:`traversal`. Read :ref:`traversal_chapter` to understand the concept
- of a view name.
+ argument is typically only used when your application uses :term:`traversal`.
+ Read :ref:`traversal_chapter` to understand the concept of a view name.
If ``name`` is not supplied, the empty string is used (implying the default
view).
``context``
- An object representing a Python class that the :term:`context` resource
- must be an instance of *or* the :term:`interface` that the :term:`context`
+ An object representing a Python class of which the :term:`context` resource
+ must be an instance *or* the :term:`interface` that the :term:`context`
resource must provide in order for this view to be found and called. This
predicate is true when the :term:`context` resource is an instance of the
- represented class or if the :term:`context` resource provides the
- represented interface; it is otherwise false.
+ represented class or if the :term:`context` resource provides the represented
+ interface; it is otherwise false.
If ``context`` is not supplied, the value ``None``, which matches any
resource, is used.
@@ -274,68 +284,68 @@ configured view.
the named route has matched.
This value must match the ``name`` of a :term:`route configuration`
- declaration (see :ref:`urldispatch_chapter`) that must match before this
- view will be called. Note that the ``route`` configuration referred to by
+ declaration (see :ref:`urldispatch_chapter`) that must match before this view
+ will be called. Note that the ``route`` configuration referred to by
``route_name`` will usually have a ``*traverse`` token in the value of its
``pattern``, representing a part of the path that will be used by
:term:`traversal` against the result of the route's :term:`root factory`.
If ``route_name`` is not supplied, the view callable will only have a chance
of being invoked if no other route was matched. This is when the
- request/context pair found via :term:`resource location` does not indicate
- it matched any configured route.
+ request/context pair found via :term:`resource location` does not indicate it
+ matched any configured route.
``request_type``
This value should be an :term:`interface` that the :term:`request` must
provide in order for this view to be found and called.
- If ``request_type`` is not supplied, the value ``None`` is used, implying
- any request type.
+ If ``request_type`` is not supplied, the value ``None`` is used, implying any
+ request type.
*This is an advanced feature, not often used by "civilians"*.
``request_method``
This value can be either a string (such as ``"GET"``, ``"POST"``,
- ``"PUT"``, ``"DELETE"``, ``"HEAD"`` or ``"OPTIONS"``) representing an
- HTTP ``REQUEST_METHOD``, or a tuple containing one or more of these
- strings. A view declaration with this argument ensures that the
- view will only be called when the ``method`` attribute of the
- request (aka the ``REQUEST_METHOD`` of the WSGI environment) matches
- a supplied value. Note that use of ``"GET"`` also implies that the
- view will respond to ``"HEAD"`` as of Pyramid 1.4.
+ ``"PUT"``, ``"DELETE"``, ``"HEAD"``, or ``"OPTIONS"``) representing an HTTP
+ ``REQUEST_METHOD`` or a tuple containing one or more of these strings. A
+ view declaration with this argument ensures that the view will only be called
+ when the ``method`` attribute of the request (i.e., the ``REQUEST_METHOD`` of
+ the WSGI environment) matches a supplied value.
+
+ .. versionchanged:: 1.4
+ The use of ``"GET"`` also implies that the view will respond to ``"HEAD"``.
- If ``request_method`` is not supplied, the view will be invoked regardless
- of the ``REQUEST_METHOD`` of the :term:`WSGI` environment.
+ If ``request_method`` is not supplied, the view will be invoked regardless 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
- ``GET`` or ``POST`` variable) that has a name which matches the
- supplied value.
+ ``GET`` or ``POST`` variable) that has a name which matches the supplied
+ value.
- If any value supplied has a ``=`` sign in it,
- e.g. ``request_param="foo=123"``, then the key (``foo``) must both exist
- in the ``request.params`` dictionary, *and* the value must match the right
- hand side of the expression (``123``) for the view to "match" the current
- request.
+ If any value supplied has an ``=`` sign in it, e.g.,
+ ``request_param="foo=123"``, then the key (``foo``) must both exist in the
+ ``request.params`` dictionary, *and* the value must match the right hand side
+ of the expression (``123``) for the view to "match" the current request.
If ``request_param`` is not supplied, the view will be invoked without
consideration of keys and values in the ``request.params`` dictionary.
``match_param``
- This param may be either a single string of the format "key=value" or a
- tuple containing one or more of these strings.
+ This param may be either a single string of the format "key=value" or a tuple
+ containing one or more of these strings.
This argument ensures that the view will only be called when the
- :term:`request` has key/value pairs in its :term:`matchdict` that equal
- those supplied in the predicate. e.g. ``match_param="action=edit"`` would
+ :term:`request` has key/value pairs in its :term:`matchdict` that equal those
+ supplied in the predicate. For example, ``match_param="action=edit"`` would
require the ``action`` parameter in the :term:`matchdict` match the right
hand side of the expression (``edit``) for the view to "match" the current
request.
- If the ``match_param`` is a tuple, every key/value pair must match
- for the predicate to pass.
+ If the ``match_param`` is a tuple, every key/value pair must match for the
+ predicate to pass.
If ``match_param`` is not supplied, the view will be invoked without
consideration of the keys and values in ``request.matchdict``.
@@ -343,41 +353,40 @@ configured view.
.. 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
- in order for this view to be found and called. The resources in your
- resource tree must be "location-aware" to use this feature.
+ 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 in
+ order for this view to be found and called. The resources in your resource
+ tree must be "location-aware" to use this feature.
- If ``containment`` is not supplied, the interfaces and classes in the
- lineage are not considered when deciding whether or not to invoke the view
- callable.
+ If ``containment`` is not supplied, the interfaces and classes in the lineage
+ are not considered when deciding whether or not to invoke the view callable.
See :ref:`location_aware` for more information about location-awareness.
``xhr``
This value should be either ``True`` or ``False``. If this value is
specified and is ``True``, the :term:`WSGI` environment must possess an
- ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header that has the
+ ``HTTP_X_REQUESTED_WITH`` header (i.e., ``X-Requested-With``) that has the
value ``XMLHttpRequest`` for the associated view callable to be found and
called. This is useful for detecting AJAX requests issued from jQuery,
- Prototype and other Javascript libraries.
+ Prototype, and other Javascript libraries.
- If ``xhr`` is not specified, the ``HTTP_X_REQUESTED_WITH`` HTTP header is
- not taken into consideration when deciding whether or not to invoke the
+ If ``xhr`` is not specified, the ``HTTP_X_REQUESTED_WITH`` HTTP header is not
+ taken into consideration when deciding whether or not to invoke the
associated view callable.
``accept``
- The value of this argument represents a match query for one or more
- mimetypes in the ``Accept`` HTTP request header. If this value is
- specified, it must be in one of the following forms: a mimetype match token
- in the form ``text/plain``, a wildcard mimetype match token in the form
- ``text/*`` or a match-all wildcard mimetype match token in the form
- ``*/*``. If any of the forms matches the ``Accept`` header of the request,
- this predicate will be true.
-
- If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is not
- taken into consideration when deciding whether or not to invoke the
- associated view callable.
+ The value of this argument represents a match query for one or more mimetypes
+ in the ``Accept`` HTTP request header. If this value is specified, it must
+ be in one of the following forms: a mimetype match token in the form
+ ``text/plain``, a wildcard mimetype match token in the form ``text/*``, or a
+ match-all wildcard mimetype match token in the form ``*/*``. If any of the
+ forms matches the ``Accept`` header of the request, this predicate will be
+ true.
+
+ If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is not taken
+ into consideration when deciding whether or not to invoke the associated view
+ callable.
``header``
This value represents an HTTP header name or a header name/value pair.
@@ -385,94 +394,92 @@ configured view.
If ``header`` is specified, it must be a header name or a
``headername:headervalue`` pair.
- If ``header`` is specified without a value (a bare header name only,
- e.g. ``If-Modified-Since``), the view will only be invoked if the HTTP
- header exists with any value in the request.
+ If ``header`` is specified without a value (a bare header name only, e.g.,
+ ``If-Modified-Since``), the view will only be invoked if the HTTP header
+ exists with any value in the request.
- If ``header`` is specified, and possesses a name/value pair
- (e.g. ``User-Agent:Mozilla/.*``), the view will only be invoked if the HTTP
- header exists *and* the HTTP header matches the value requested. When the
- ``headervalue`` contains a ``:`` (colon), it will be considered a
- name/value pair (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``).
- The value portion should be a regular expression.
+ If ``header`` is specified, and possesses a name/value pair (e.g.,
+ ``User-Agent:Mozilla/.*``), the view will only be invoked if the HTTP header
+ exists *and* the HTTP header matches the value requested. When the
+ ``headervalue`` contains a ``:`` (colon), it will be considered a name/value
+ pair (e.g., ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The value
+ portion should be a regular expression.
Whether or not the value represents a header name or a header name/value
pair, the case of the header name is not significant.
- If ``header`` is not specified, the composition, presence or absence of
- HTTP headers is not taken into consideration when deciding whether or not
- to invoke the associated view callable.
+ If ``header`` is not specified, the composition, presence, or absence of HTTP
+ headers is not taken into consideration when deciding whether or not to
+ invoke the associated view callable.
``path_info``
This value represents a regular expression pattern that will be tested
- against the ``PATH_INFO`` WSGI environment variable to decide whether or
- not to call the associated view callable. If the regex matches, this
- predicate will be ``True``.
+ against the ``PATH_INFO`` WSGI environment variable to decide whether or not
+ to call the associated view callable. If the regex matches, this predicate
+ will be ``True``.
If ``path_info`` is not specified, the WSGI ``PATH_INFO`` is not taken into
consideration when deciding whether or not to invoke the associated view
callable.
``check_csrf``
- If specified, this value should be one of ``None``, ``True``, ``False``, or
- a string representing the 'check name'. If the value is ``True`` or a
- string, CSRF checking will be performed. If the value is ``False`` or
- ``None``, CSRF checking will not be performed.
+ If specified, this value should be one of ``None``, ``True``, ``False``, or a
+ string representing the "check name". If the value is ``True`` or a string,
+ CSRF checking will be performed. If the value is ``False`` or ``None``, CSRF
+ checking will not be performed.
- If the value provided is a string, that string will be used as the 'check
- name'. If the value provided is ``True``, ``csrf_token`` will be used as
- the check name.
+ If the value provided is a string, that string will be used as the "check
+ name". If the value provided is ``True``, ``csrf_token`` will be used as the
+ check name.
If CSRF checking is performed, the checked value will be the value of
``request.params[check_name]``. This value will be compared against the
value of ``request.session.get_csrf_token()``, and the check will pass if
- these two values are the same. If the check passes, the associated view
- will be permitted to execute. If the check fails, the associated view
- will not be permitted to execute.
+ these two values are the same. If the check passes, the associated view will
+ be permitted to execute. If the check fails, the associated view will not be
+ permitted to execute.
- Note that using this feature requires a :term:`session factory` to have
- been configured.
+ Note that using this feature requires a :term:`session factory` to have been
+ configured.
.. versionadded:: 1.4a2
``physical_path``
If specified, this value should be a string or a tuple representing the
:term:`physical path` of the context found via traversal for this predicate
- to match as true. For example: ``physical_path='/'`` or
- ``physical_path='/a/b/c'`` or ``physical_path=('', 'a', 'b', 'c')``. This is
- not a path prefix match or a regex, it's a whole-path match. It's useful
+ to match as true. For example, ``physical_path='/'``,
+ ``physical_path='/a/b/c'``, or ``physical_path=('', 'a', 'b', 'c')``. This
+ is not a path prefix match or a regex, but a whole-path match. It's useful
when you want to always potentially show a view when some object is traversed
to, but you can't be sure about what kind of object it will be, so you can't
- use the ``context`` predicate. The individual path elements inbetween slash
+ use the ``context`` predicate. The individual path elements between slash
characters or in tuple elements should be the Unicode representation of the
name of the resource and should not be encoded in any way.
.. versionadded:: 1.4a3
``effective_principals``
-
If specified, this value should be a :term:`principal` identifier or a
sequence of principal identifiers. If the
- :meth:`pyramid.request.Request.effective_principals` method indicates that every
- principal named in the argument list is present in the current request, this
- predicate will return True; otherwise it will return False. For example:
- ``effective_principals=pyramid.security.Authenticated`` or
+ :meth:`pyramid.request.Request.effective_principals` method indicates that
+ every principal named in the argument list is present in the current request,
+ this predicate will return True; otherwise it will return False. For
+ example: ``effective_principals=pyramid.security.Authenticated`` or
``effective_principals=('fred', 'group:admins')``.
.. versionadded:: 1.4a4
``custom_predicates``
- If ``custom_predicates`` is specified, it must be a sequence of references
- to custom predicate callables. Use custom predicates when no set of
- predefined predicates do what you need. Custom predicates can be combined
- with predefined predicates as necessary. Each custom predicate callable
- should accept two arguments: ``context`` and ``request`` and should return
- either ``True`` or ``False`` after doing arbitrary evaluation of the
- context resource and/or the request. If all callables return ``True``, the
+ If ``custom_predicates`` is specified, it must be a sequence of references to
+ custom predicate callables. Use custom predicates when no set of predefined
+ predicates do what you need. Custom predicates can be combined with
+ predefined predicates as necessary. Each custom predicate callable should
+ accept two arguments, ``context`` and ``request``, and should return either
+ ``True`` or ``False`` after doing arbitrary evaluation of the context
+ resource and/or the request. If all callables return ``True``, the
associated view callable will be considered viable for a given request.
- If ``custom_predicates`` is not specified, no custom predicates are
- used.
+ If ``custom_predicates`` is not specified, no custom predicates are used.
``predicates``
Pass a key/value pair here to use a third-party predicate registered via
@@ -500,8 +507,8 @@ You can invert the meaning of any predicate value by wrapping it in a call to
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.
+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:
@@ -523,11 +530,11 @@ Adding View Configuration Using the ``@view_config`` Decorator
.. warning::
- Using this feature tends to slow down application startup slightly, as
- more work is performed at application startup to scan for view
- configuration declarations. For maximum startup performance, use the view
- configuration method described in
- :ref:`mapping_views_using_imperative_config_section` instead.
+ Using this feature tends to slow down application startup slightly, as more
+ work is performed at application startup to scan for view configuration
+ declarations. For maximum startup performance, use the view configuration
+ method described in :ref:`mapping_views_using_imperative_config_section`
+ instead.
The :class:`~pyramid.view.view_config` decorator can be used to associate
:term:`view configuration` information with a function, method, or class that
@@ -577,9 +584,8 @@ request method, request type, request param, route name, or containment.
The mere existence of a ``@view_config`` decorator doesn't suffice to perform
view configuration. All that the decorator does is "annotate" the function
with your configuration declarations, it doesn't process them. To make
-:app:`Pyramid` process your :class:`pyramid.view.view_config` declarations,
-you *must* use the ``scan`` method of a
-:class:`pyramid.config.Configurator`:
+:app:`Pyramid` process your :class:`pyramid.view.view_config` declarations, you
+*must* use the ``scan`` method of a :class:`pyramid.config.Configurator`:
.. code-block:: python
:linenos:
@@ -588,9 +594,9 @@ you *must* use the ``scan`` method of a
# pyramid.config.Configurator class
config.scan()
-Please see :ref:`decorations_and_code_scanning` for detailed information
-about what happens when code is scanned for configuration declarations
-resulting from use of decorators like :class:`~pyramid.view.view_config`.
+Please see :ref:`decorations_and_code_scanning` for detailed information about
+what happens when code is scanned for configuration declarations resulting from
+use of decorators like :class:`~pyramid.view.view_config`.
See :ref:`configuration_module` for additional API arguments to the
:meth:`~pyramid.config.Configurator.scan` method. For example, the method
@@ -598,10 +604,10 @@ allows you to supply a ``package`` argument to better control exactly *which*
code will be scanned.
All arguments to the :class:`~pyramid.view.view_config` decorator mean
-precisely the same thing as they would if they were passed as arguments to
-the :meth:`pyramid.config.Configurator.add_view` method save for the ``view``
-argument. Usage of the :class:`~pyramid.view.view_config` decorator is a
-form of :term:`declarative configuration`, while
+precisely the same thing as they would if they were passed as arguments to the
+:meth:`pyramid.config.Configurator.add_view` method save for the ``view``
+argument. Usage of the :class:`~pyramid.view.view_config` decorator is a form
+of :term:`declarative configuration`, while
:meth:`pyramid.config.Configurator.add_view` is a form of :term:`imperative
configuration`. However, they both do the same thing.
@@ -629,8 +635,8 @@ If your view callable is a function, it may be used as a function decorator:
return Response('edited!')
If your view callable is a class, the decorator can also be used as a class
-decorator. All the arguments to the decorator are the same when applied
-against a class as when they are applied against a function. For example:
+decorator. All the arguments to the decorator are the same when applied against
+a class as when they are applied against a function. For example:
.. code-block:: python
:linenos:
@@ -681,15 +687,15 @@ The decorator can also be used against a method of a class:
When the decorator is used against a method of a class, a view is registered
for the *class*, so the class constructor must accept an argument list in one
-of two forms: either it must accept a single argument ``request`` or it must
-accept two arguments, ``context, request``.
+of two forms: either a single argument, ``request``, or two arguments,
+``context, request``.
The method which is decorated must return a :term:`response`.
Using the decorator against a particular method of a class is equivalent to
-using the ``attr`` parameter in a decorator attached to the class itself.
-For example, the above registration implied by the decorator being used
-against the ``amethod`` method could be spelled equivalently as the below:
+using the ``attr`` parameter in a decorator attached to the class itself. For
+example, the above registration implied by the decorator being used against the
+``amethod`` method could be written equivalently as follows:
.. code-block:: python
:linenos:
@@ -715,9 +721,9 @@ Adding View Configuration Using :meth:`~pyramid.config.Configurator.add_view`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :meth:`pyramid.config.Configurator.add_view` method within
-:ref:`configuration_module` is used to configure a view "imperatively"
-(without a :class:`~pyramid.view.view_config` decorator). The arguments to
-this method are very similar to the arguments that you provide to the
+:ref:`configuration_module` is used to configure a view "imperatively" (without
+a :class:`~pyramid.view.view_config` decorator). The arguments to this method
+are very similar to the arguments that you provide to the
:class:`~pyramid.view.view_config` decorator. For example:
.. code-block:: python
@@ -732,10 +738,10 @@ this method are very similar to the arguments that you provide to the
# pyramid.config.Configurator class
config.add_view(hello_world, route_name='hello')
-The first argument, a :term:`view callable`, is the only required argument.
-It must either be a Python object which is the view itself or a
-:term:`dotted Python name` to such an object.
-In the above example, the ``view callable`` is the ``hello_world`` function.
+The first argument, a :term:`view callable`, is the only required argument. It
+must either be a Python object which is the view itself or a :term:`dotted
+Python name` to such an object. In the above example, the ``view callable`` is
+the ``hello_world`` function.
When you use only :meth:`~pyramid.config.Configurator.add_view` to add view
configurations, you don't need to issue a :term:`scan` in order for the view
@@ -757,7 +763,7 @@ defaults to the view configuration information used by every ``@view_config``
decorator that decorates a method of that class.
For instance, if you've got a class that has methods that represent "REST
-actions", all which are mapped to the same route, but different request
+actions", all of which are mapped to the same route but different request
methods, instead of this:
.. code-block:: python
@@ -809,17 +815,17 @@ You can do this:
return Response('delete')
In the above example, we were able to take the ``route_name='rest'`` argument
-out of the call to each individual ``@view_config`` statement, because we
-used a ``@view_defaults`` class decorator to provide the argument as a
-default to each view method it possessed.
+out of the call to each individual ``@view_config`` statement because we used a
+``@view_defaults`` class decorator to provide the argument as a default to each
+view method it possessed.
Arguments passed to ``@view_config`` will override any default passed to
``@view_defaults``.
The ``view_defaults`` class decorator can also provide defaults to the
:meth:`pyramid.config.Configurator.add_view` directive when a decorated class
-is passed to that directive as its ``view`` argument. For example, instead
-of this:
+is passed to that directive as its ``view`` argument. For example, instead of
+this:
.. code-block:: python
:linenos:
@@ -853,7 +859,7 @@ of this:
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:
+decorator on the ``RESTView`` class:
.. code-block:: python
:linenos:
@@ -889,8 +895,8 @@ decorator on the RESTView class:
argument passed to ``view_defaults`` provides a default for the view
configurations of methods of the class it's decorating.
-Normal Python inheritance rules apply to defaults added via
-``view_defaults``. For example:
+Normal Python inheritance rules apply to defaults added via ``view_defaults``.
+For example:
.. code-block:: python
:linenos:
@@ -904,8 +910,8 @@ Normal Python inheritance rules apply to defaults added via
The ``Bar`` class above will inherit its view defaults from the arguments
passed to the ``view_defaults`` decorator of the ``Foo`` class. To prevent
-this from happening, use a ``view_defaults`` decorator without any arguments
-on the subclass:
+this from happening, use a ``view_defaults`` decorator without any arguments on
+the subclass:
.. code-block:: python
:linenos:
@@ -931,11 +937,11 @@ Configuring View Security
~~~~~~~~~~~~~~~~~~~~~~~~~
If an :term:`authorization policy` is active, any :term:`permission` attached
-to a :term:`view configuration` found during view lookup will be verified.
-This will ensure that the currently authenticated user possesses that
-permission against the :term:`context` resource before the view function is
-actually called. Here's an example of specifying a permission in a view
-configuration using :meth:`~pyramid.config.Configurator.add_view`:
+to a :term:`view configuration` found during view lookup will be verified. This
+will ensure that the currently authenticated user possesses that permission
+against the :term:`context` resource before the view function is actually
+called. Here's an example of specifying a permission in a view configuration
+using :meth:`~pyramid.config.Configurator.add_view`:
.. code-block:: python
:linenos:
@@ -949,8 +955,8 @@ configuration using :meth:`~pyramid.config.Configurator.add_view`:
When an :term:`authorization policy` is enabled, this view will be protected
with the ``add`` permission. The view will *not be called* if the user does
not possess the ``add`` permission relative to the current :term:`context`.
-Instead the :term:`forbidden view` result will be returned to the client as
-per :ref:`protecting_views`.
+Instead the :term:`forbidden view` result will be returned to the client as per
+:ref:`protecting_views`.
.. index::
single: debugging not found errors
@@ -961,14 +967,14 @@ per :ref:`protecting_views`.
:exc:`~pyramid.exceptions.NotFound` Errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-It's useful to be able to debug :exc:`~pyramid.exceptions.NotFound`
-error responses when they
-occur unexpectedly due to an application registry misconfiguration. To debug
-these errors, use the ``PYRAMID_DEBUG_NOTFOUND`` environment variable or the
-``pyramid.debug_notfound`` configuration file setting. Details of why a view
-was not found will be printed to ``stderr``, and the browser representation of
-the error will include the same information. See :ref:`environment_chapter`
-for more information about how, and where to set these values.
+It's useful to be able to debug :exc:`~pyramid.exceptions.NotFound` error
+responses when they occur unexpectedly due to an application registry
+misconfiguration. To debug these errors, use the ``PYRAMID_DEBUG_NOTFOUND``
+environment variable or the ``pyramid.debug_notfound`` configuration file
+setting. Details of why a view was not found will be printed to ``stderr``,
+and the browser representation of the error will include the same information.
+See :ref:`environment_chapter` for more information about how, and where to set
+these values.
.. index::
single: HTTP caching
@@ -980,23 +986,23 @@ Influencing HTTP Caching
.. versionadded:: 1.1
-When a non-``None`` ``http_cache`` argument is passed to a view
-configuration, Pyramid will set ``Expires`` and ``Cache-Control`` response
-headers in the resulting response, causing browsers to cache the response
-data for some time. See ``http_cache`` in :ref:`nonpredicate_view_args` for
-the allowable values and what they mean.
-
-Sometimes it's undesirable to have these headers set as the result of
-returning a response from a view, even though you'd like to decorate the view
-with a view configuration decorator that has ``http_cache``. Perhaps there's
-an alternate branch in your view code that returns a response that should
-never be cacheable, while the "normal" branch returns something that should
-always be cacheable. If this is the case, set the ``prevent_auto`` attribute
-of the ``response.cache_control`` object to a non-``False`` value. For
-example, the below view callable is configured with a ``@view_config``
-decorator that indicates any response from the view should be cached for 3600
-seconds. However, the view itself prevents caching from taking place unless
-there's a ``should_cache`` GET or POST variable:
+When a non-``None`` ``http_cache`` argument is passed to a view configuration,
+Pyramid will set ``Expires`` and ``Cache-Control`` response headers in the
+resulting response, causing browsers to cache the response data for some time.
+See ``http_cache`` in :ref:`nonpredicate_view_args` for the allowable values
+and what they mean.
+
+Sometimes it's undesirable to have these headers set as the result of returning
+a response from a view, even though you'd like to decorate the view with a view
+configuration decorator that has ``http_cache``. Perhaps there's an
+alternative branch in your view code that returns a response that should never
+be cacheable, while the "normal" branch returns something that should always be
+cacheable. If this is the case, set the ``prevent_auto`` attribute of the
+``response.cache_control`` object to a non-``False`` value. For example, the
+below view callable is configured with a ``@view_config`` decorator that
+indicates any response from the view should be cached for 3600 seconds.
+However, the view itself prevents caching from taking place unless there's a
+``should_cache`` GET or POST variable:
.. code-block:: python
@@ -1005,23 +1011,23 @@ there's a ``should_cache`` GET or POST variable:
@view_config(http_cache=3600)
def view(request):
response = Response()
- if not 'should_cache' in request.params:
+ if 'should_cache' not in request.params:
response.cache_control.prevent_auto = True
return response
-Note that the ``http_cache`` machinery will overwrite or add to caching
-headers you set within the view itself unless you use ``prevent_auto``.
+Note that the ``http_cache`` machinery will overwrite or add to caching headers
+you set within the view itself, unless you use ``prevent_auto``.
-You can also turn off the effect of ``http_cache`` entirely for the duration
-of a Pyramid application lifetime. To do so, set the
+You can also turn off the effect of ``http_cache`` entirely for the duration of
+a Pyramid application lifetime. To do so, set the
``PYRAMID_PREVENT_HTTP_CACHE`` environment variable or the
-``pyramid.prevent_http_cache`` configuration value setting to a true value.
-For more information, see :ref:`preventing_http_caching`.
+``pyramid.prevent_http_cache`` configuration value setting to a true value. For
+more information, see :ref:`preventing_http_caching`.
Note that setting ``pyramid.prevent_http_cache`` will have no effect on caching
headers that your application code itself sets. It will only prevent caching
-headers that would have been set by the Pyramid HTTP caching machinery
-invoked as the result of the ``http_cache`` argument to view configuration.
+headers that would have been set by the Pyramid HTTP caching machinery invoked
+as the result of the ``http_cache`` argument to view configuration.
.. index::
pair: view configuration; debugging
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index a746eb043..770d27919 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -4,23 +4,22 @@ Views
=====
One of the primary jobs of :app:`Pyramid` is to find and invoke a :term:`view
-callable` when a :term:`request` reaches your application. View callables
-are bits of code which do something interesting in response to a request made
-to your application. They are the "meat" of any interesting web application.
+callable` when a :term:`request` reaches your application. View callables are
+bits of code which do something interesting in response to a request made to
+your application. They are the "meat" of any interesting web application.
-.. note::
+.. note::
A :app:`Pyramid` :term:`view callable` is often referred to in
- conversational shorthand as a :term:`view`. In this documentation,
- however, we need to use less ambiguous terminology because there
- are significant differences between view *configuration*, the code
- that implements a view *callable*, and the process of view
- *lookup*.
+ conversational shorthand as a :term:`view`. In this documentation, however,
+ we need to use less ambiguous terminology because there are significant
+ differences between view *configuration*, the code that implements a view
+ *callable*, and the process of view *lookup*.
-This chapter describes how view callables should be defined. We'll have to
-wait until a following chapter (entitled :ref:`view_config_chapter`) to find
-out how we actually tell :app:`Pyramid` to wire up view callables to
-particular URL patterns and other request circumstances.
+This chapter describes how view callables should be defined. We'll have to wait
+until a following chapter (entitled :ref:`view_config_chapter`) to find out how
+we actually tell :app:`Pyramid` to wire up view callables to particular URL
+patterns and other request circumstances.
.. index::
single: view callables
@@ -28,21 +27,21 @@ particular URL patterns and other request circumstances.
View Callables
--------------
-View callables are, at the risk of sounding obvious, callable Python
-objects. Specifically, view callables can be functions, classes, or instances
-that implement a ``__call__`` method (making the instance callable).
+View callables are, at the risk of sounding obvious, callable Python objects.
+Specifically, view callables can be functions, classes, or instances that
+implement a ``__call__`` method (making the instance callable).
-View callables must, at a minimum, accept a single argument named
-``request``. This argument represents a :app:`Pyramid` :term:`Request`
-object. A request object represents a :term:`WSGI` environment provided to
-:app:`Pyramid` by the upstream WSGI server. As you might expect, the request
-object contains everything your application needs to know about the specific
-HTTP request being made.
+View callables must, at a minimum, accept a single argument named ``request``.
+This argument represents a :app:`Pyramid` :term:`Request` object. A request
+object represents a :term:`WSGI` environment provided to :app:`Pyramid` by the
+upstream WSGI server. As you might expect, the request object contains
+everything your application needs to know about the specific HTTP request being
+made.
A view callable's ultimate responsibility is to create a :app:`Pyramid`
-:term:`Response` object. This can be done by creating a :term:`Response`
-object in the view callable code and returning it directly or by raising
-special kinds of exceptions from within the body of a view callable.
+:term:`Response` object. This can be done by creating a :term:`Response` object
+in the view callable code and returning it directly or by raising special kinds
+of exceptions from within the body of a view callable.
.. index::
single: view calling convention
@@ -76,17 +75,17 @@ Defining a View Callable as a Class
-----------------------------------
A view callable may also be represented by a Python class instead of a
-function. When a view callable is a class, the calling semantics are
-slightly different than when it is a function or another non-class callable.
-When a view callable is a class, the class' ``__init__`` method is called with a
+function. When a view callable is a class, the calling semantics are slightly
+different than when it is a function or another non-class callable. When a view
+callable is a class, the class's ``__init__`` method is called with a
``request`` parameter. As a result, an instance of the class is created.
Subsequently, that instance's ``__call__`` method is invoked with no
-parameters. Views defined as classes must have the following traits:
+parameters. Views defined as classes must have the following traits.
-- an ``__init__`` method that accepts a ``request`` argument.
+- an ``__init__`` method that accepts a ``request`` argument
-- a ``__call__`` (or other) method that accepts no parameters and which
- returns a response.
+- a ``__call__`` (or other) method that accepts no parameters and which returns
+ a response
For example:
@@ -106,12 +105,12 @@ The request object passed to ``__init__`` is the same type of request object
described in :ref:`function_as_view`.
If you'd like to use a different attribute than ``__call__`` to represent the
-method expected to return a response, you can use an ``attr`` value as part
-of the configuration for the view. See :ref:`view_configuration_parameters`.
-The same view callable class can be used in different view configuration
-statements with different ``attr`` values, each pointing at a different
-method of the class if you'd like the class to represent a collection of
-related view callables.
+method expected to return a response, you can use an ``attr`` value as part of
+the configuration for the view. See :ref:`view_configuration_parameters`. The
+same view callable class can be used in different view configuration statements
+with different ``attr`` values, each pointing at a different method of the
+class if you'd like the class to represent a collection of related view
+callables.
.. index::
single: view response
@@ -135,11 +134,11 @@ implements the :term:`Response` interface is to return a
def view(request):
return Response('OK')
-:app:`Pyramid` provides a range of different "exception" classes which
-inherit from :class:`pyramid.response.Response`. For example, an instance of
-the class :class:`pyramid.httpexceptions.HTTPFound` is also a valid response
-object because it inherits from :class:`~pyramid.response.Response`. For
-examples, see :ref:`http_exceptions` and :ref:`http_redirect`.
+:app:`Pyramid` provides a range of different "exception" classes which inherit
+from :class:`pyramid.response.Response`. For example, an instance of the class
+:class:`pyramid.httpexceptions.HTTPFound` is also a valid response object
+because it inherits from :class:`~pyramid.response.Response`. For examples,
+see :ref:`http_exceptions` and :ref:`http_redirect`.
.. note::
@@ -155,7 +154,7 @@ examples, see :ref:`http_exceptions` and :ref:`http_redirect`.
.. _special_exceptions_in_callables:
-Using Special Exceptions In View Callables
+Using Special Exceptions in View Callables
------------------------------------------
Usually when a Python exception is raised within a view callable,
@@ -176,14 +175,14 @@ exception` objects.
HTTP Exceptions
~~~~~~~~~~~~~~~
-All :mod:`pyramid.httpexceptions` classes which are documented
-as inheriting from the :class:`pyramid.httpexceptions.HTTPException` are
-:term:`http exception` objects. Instances of an HTTP exception object may
-either be *returned* or *raised* from within view code. In either case
-(return or raise) the instance will be used as the view's response.
+All :mod:`pyramid.httpexceptions` classes which are documented as inheriting
+from the :class:`pyramid.httpexceptions.HTTPException` are :term:`http
+exception` objects. Instances of an HTTP exception object may either be
+*returned* or *raised* from within view code. In either case (return or raise)
+the instance will be used as the view's response.
-For example, the :class:`pyramid.httpexceptions.HTTPUnauthorized` exception
-can be raised. This will cause a response to be generated with a ``401
+For example, the :class:`pyramid.httpexceptions.HTTPUnauthorized` exception can
+be raised. This will cause a response to be generated with a ``401
Unauthorized`` status:
.. code-block:: python
@@ -194,8 +193,8 @@ Unauthorized`` status:
def aview(request):
raise HTTPUnauthorized()
-An HTTP exception, instead of being raised, can alternately be *returned*
-(HTTP exceptions are also valid response objects):
+An HTTP exception, instead of being raised, can alternately be *returned* (HTTP
+exceptions are also valid response objects):
.. code-block:: python
:linenos:
@@ -207,11 +206,11 @@ An HTTP exception, instead of being raised, can alternately be *returned*
A shortcut for creating an HTTP exception is the
:func:`pyramid.httpexceptions.exception_response` function. This function
-accepts an HTTP status code and returns the corresponding HTTP exception.
-For example, instead of importing and constructing a
-:class:`~pyramid.httpexceptions.HTTPUnauthorized` response object, you can
-use the :func:`~pyramid.httpexceptions.exception_response` function to
-construct and return the same object.
+accepts an HTTP status code and returns the corresponding HTTP exception. For
+example, instead of importing and constructing a
+:class:`~pyramid.httpexceptions.HTTPUnauthorized` response object, you can use
+the :func:`~pyramid.httpexceptions.exception_response` function to construct
+and return the same object.
.. code-block:: python
:linenos:
@@ -223,8 +222,8 @@ construct and return the same object.
This is the case because ``401`` is the HTTP status code for "HTTP
Unauthorized". Therefore, ``raise exception_response(401)`` is functionally
-equivalent to ``raise HTTPUnauthorized()``. Documentation which maps each
-HTTP response code to its purpose and its associated HTTP exception object is
+equivalent to ``raise HTTPUnauthorized()``. Documentation which maps each HTTP
+response code to its purpose and its associated HTTP exception object is
provided within :mod:`pyramid.httpexceptions`.
.. versionadded:: 1.1
@@ -233,22 +232,22 @@ provided within :mod:`pyramid.httpexceptions`.
How Pyramid Uses HTTP Exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-HTTP exceptions are meant to be used directly by application
-developers. However, Pyramid itself will raise two HTTP exceptions at
-various points during normal operations:
+HTTP exceptions are meant to be used directly by application developers.
+However, Pyramid itself will raise two HTTP exceptions at various points during
+normal operations.
-* :exc:`~pyramid.httpexceptions.HTTPNotFound`
- gets raised when a view to service a request is not found.
-* :exc:`~pyramid.httpexceptions.HTTPForbidden`
- gets raised when authorization was forbidden by a security policy.
+* :exc:`~pyramid.httpexceptions.HTTPNotFound` gets raised when a view to
+ service a request is not found.
+* :exc:`~pyramid.httpexceptions.HTTPForbidden` gets raised when authorization
+ was forbidden by a security policy.
If :exc:`~pyramid.httpexceptions.HTTPNotFound` is raised by Pyramid itself or
-within view code, the result of the :term:`Not Found View` will be returned
-to the user agent which performed the request.
+within view code, the result of the :term:`Not Found View` will be returned to
+the user agent which performed the request.
If :exc:`~pyramid.httpexceptions.HTTPForbidden` is raised by Pyramid itself
-within view code, the result of the :term:`Forbidden View` will be returned
-to the user agent which performed the request.
+within view code, the result of the :term:`Forbidden View` will be returned to
+the user agent which performed the request.
.. index::
single: exception views
@@ -266,7 +265,7 @@ responses.
To register a view that should be called whenever a particular exception is
raised from within :app:`Pyramid` view code, use the exception class (or one of
its superclasses) as the :term:`context` of a view configuration which points
-at a view callable you'd like to generate a response for.
+at a view callable for which you'd like to generate a response.
For example, given the following exception class in a module named
``helloworld.exceptions``:
@@ -300,8 +299,8 @@ view callable will be invoked whenever a
view code. The same exception raised by a custom root factory, a custom
traverser, or a custom view or route predicate is also caught and hooked.
-Other normal view predicates can also be used in combination with an
-exception view registration:
+Other normal view predicates can also be used in combination with an exception
+view registration:
.. code-block:: python
:linenos:
@@ -315,24 +314,24 @@ exception view registration:
response.status_int = 500
return response
-The above exception view names the ``route_name`` of ``home``, meaning that
-it will only be called when the route matched has a name of ``home``. You
-can therefore have more than one exception view for any given exception in
-the system: the "most specific" one will be called when the set of request
+The above exception view names the ``route_name`` of ``home``, meaning that it
+will only be called when the route matched has a name of ``home``. You can
+therefore have more than one exception view for any given exception in the
+system: the "most specific" one will be called when the set of request
circumstances match the view registration.
-The only view predicate that cannot be used successfully when creating
-an exception view configuration is ``name``. The name used to look up
-an exception view is always the empty string. Views registered as
-exception views which have a name will be ignored.
+The only view predicate that cannot be used successfully when creating an
+exception view configuration is ``name``. The name used to look up an
+exception view is always the empty string. Views registered as exception views
+which have a name will be ignored.
.. note::
- Normal (i.e., non-exception) views registered against a context resource
- type which inherits from :exc:`Exception` will work normally. When an
- exception view configuration is processed, *two* views are registered. One
- as a "normal" view, the other as an "exception" view. This means that you
- can use an exception as ``context`` for a normal view.
+ Normal (i.e., non-exception) views registered against a context resource type
+ which inherits from :exc:`Exception` will work normally. When an exception
+ view configuration is processed, *two* views are registered. One as a
+ "normal" view, the other as an "exception" view. This means that you can use
+ an exception as ``context`` for a normal view.
Exception views can be configured with any view registration mechanism:
``@view_config`` decorator or imperative ``add_view`` styles.
@@ -340,9 +339,9 @@ Exception views can be configured with any view registration mechanism:
.. note::
Pyramid's :term:`exception view` handling logic is implemented as a tween
- factory function: :func:`pyramid.tweens.excview_tween_factory`. If
- Pyramid exception view handling is desired, and tween factories are
- specified via the ``pyramid.tweens`` configuration setting, the
+ factory function: :func:`pyramid.tweens.excview_tween_factory`. If Pyramid
+ exception view handling is desired, and tween factories are specified via
+ the ``pyramid.tweens`` configuration setting, the
:func:`pyramid.tweens.excview_tween_factory` function must be added to the
``pyramid.tweens`` configuration setting list explicitly. If it is not
present, Pyramid will not perform exception view handling.
@@ -358,11 +357,9 @@ Using a View Callable to do an HTTP Redirect
You can issue an HTTP redirect by using the
:class:`pyramid.httpexceptions.HTTPFound` class. Raising or returning an
-instance of this class will cause the client to receive a "302 Found"
-response.
+instance of this class will cause the client to receive a "302 Found" response.
-To do so, you can *return* a :class:`pyramid.httpexceptions.HTTPFound`
-instance.
+To do so, you can *return* a :class:`pyramid.httpexceptions.HTTPFound` instance.
.. code-block:: python
:linenos:
@@ -400,32 +397,31 @@ submission data using the :term:`WebOb` API, see :ref:`webob_chapter` and
`"Query and POST variables" within the WebOb documentation
<http://docs.webob.org/en/latest/reference.html#query-post-variables>`_.
:app:`Pyramid` defers to WebOb for its request and response implementations,
-and handling form submission data is a property of the request
-implementation. Understanding WebOb's request API is the key to
-understanding how to process form submission data.
-
-There are some defaults that you need to be aware of when trying to handle
-form submission data in a :app:`Pyramid` view. Having high-order (i.e.,
-non-ASCII) characters in data contained within form submissions is
-exceedingly common, and the UTF-8 encoding is the most common encoding used
-on the web for character data. Since Unicode values are much saner than
-working with and storing bytestrings, :app:`Pyramid` configures the
-:term:`WebOb` request machinery to attempt to decode form submission values
-into Unicode from UTF-8 implicitly. This implicit decoding happens when view
-code obtains form field values via the ``request.params``, ``request.GET``,
-or ``request.POST`` APIs (see :ref:`request_module` for details about these
-APIs).
+and handling form submission data is a property of the request implementation.
+Understanding WebOb's request API is the key to understanding how to process
+form submission data.
+
+There are some defaults that you need to be aware of when trying to handle form
+submission data in a :app:`Pyramid` view. Having high-order (i.e., non-ASCII)
+characters in data contained within form submissions is exceedingly common, and
+the UTF-8 encoding is the most common encoding used on the web for character
+data. Since Unicode values are much saner than working with and storing
+bytestrings, :app:`Pyramid` configures the :term:`WebOb` request machinery to
+attempt to decode form submission values into Unicode from UTF-8 implicitly.
+This implicit decoding happens when view code obtains form field values via the
+``request.params``, ``request.GET``, or ``request.POST`` APIs (see
+:ref:`request_module` for details about these APIs).
.. note::
- Many people find the difference between Unicode and UTF-8 confusing.
- Unicode is a standard for representing text that supports most of the
- world's writing systems. However, there are many ways that Unicode data
- can be encoded into bytes for transit and storage. UTF-8 is a specific
- encoding for Unicode, that is backwards-compatible with ASCII. This makes
- UTF-8 very convenient for encoding data where a large subset of that data
- is ASCII characters, which is largely true on the web. UTF-8 is also the
- standard character encoding for URLs.
+ Many people find the difference between Unicode and UTF-8 confusing. Unicode
+ is a standard for representing text that supports most of the world's
+ writing systems. However, there are many ways that Unicode data can be
+ encoded into bytes for transit and storage. UTF-8 is a specific encoding for
+ Unicode that is backwards-compatible with ASCII. This makes UTF-8 very
+ convenient for encoding data where a large subset of that data is ASCII
+ characters, which is largely true on the web. UTF-8 is also the standard
+ character encoding for URLs.
As an example, let's assume that the following form page is served up to a
browser client, and its ``action`` points at some :app:`Pyramid` view code:
@@ -450,8 +446,8 @@ browser client, and its ``action`` points at some :app:`Pyramid` view code:
The ``myview`` view code in the :app:`Pyramid` application *must* expect that
the values returned by ``request.params`` will be of type ``unicode``, as
-opposed to type ``str``. The following will work to accept a form post from
-the above form:
+opposed to type ``str``. The following will work to accept a form post from the
+above form:
.. code-block:: python
:linenos:
@@ -479,31 +475,31 @@ encoding of UTF-8. This can be done via a response that has a
with a ``meta http-equiv`` tag that implies that the charset is UTF-8 within
the HTML ``head`` of the page containing the form. This must be done
explicitly because all known browser clients assume that they should encode
-form data in the same character set implied by ``Content-Type`` value of the
-response containing the form when subsequently submitting that form. There is
-no other generally accepted way to tell browser clients which charset to use
-to encode form data. If you do not specify an encoding explicitly, the
-browser client will choose to encode form data in its default character set
-before submitting it, which may not be UTF-8 as the server expects. If a
-request containing form data encoded in a non-UTF8 charset is handled by your
-view code, eventually the request code accessed within your view will throw
-an error when it can't decode some high-order character encoded in another
-character set within form data, e.g., when ``request.params['somename']`` is
-accessed.
+form data in the same character set implied by the ``Content-Type`` value of
+the response containing the form when subsequently submitting that form. There
+is no other generally accepted way to tell browser clients which charset to use
+to encode form data. If you do not specify an encoding explicitly, the browser
+client will choose to encode form data in its default character set before
+submitting it, which may not be UTF-8 as the server expects. If a request
+containing form data encoded in a non-UTF-8 ``charset`` is handled by your view
+code, eventually the request code accessed within your view will throw an error
+when it can't decode some high-order character encoded in another character set
+within form data, e.g., when ``request.params['somename']`` is accessed.
If you are using the :class:`~pyramid.response.Response` class to generate a
response, or if you use the ``render_template_*`` templating APIs, the UTF-8
-charset is set automatically as the default via the ``Content-Type`` header.
-If you return a ``Content-Type`` header without an explicit charset, a
-request will add a ``;charset=utf-8`` trailer to the ``Content-Type`` header
-value for you, for response content types that are textual
-(e.g. ``text/html``, ``application/xml``, etc) as it is rendered. If you are
-using your own response object, you will need to ensure you do this yourself.
+``charset`` is set automatically as the default via the ``Content-Type``
+header. If you return a ``Content-Type`` header without an explicit
+``charset``, a request will add a ``;charset=utf-8`` trailer to the
+``Content-Type`` header value for you for response content types that are
+textual (e.g., ``text/html`` or ``application/xml``) as it is rendered. If you
+are using your own response object, you will need to ensure you do this
+yourself.
-.. note:: Only the *values* of request params obtained via
- ``request.params``, ``request.GET`` or ``request.POST`` are decoded
- to Unicode objects implicitly in the :app:`Pyramid` default
- configuration. The keys are still (byte) strings.
+.. note:: Only the *values* of request params obtained via ``request.params``,
+ ``request.GET`` or ``request.POST`` are decoded to Unicode objects
+ implicitly in the :app:`Pyramid` default configuration. The keys are still
+ (byte) strings.
.. index::
@@ -514,7 +510,7 @@ using your own response object, you will need to ensure you do this yourself.
Alternate View Callable Argument/Calling Conventions
----------------------------------------------------
-Usually, view callables are defined to accept only a single argument:
+Usually view callables are defined to accept only a single argument:
``request``. However, view callables may alternately be defined as classes,
functions, or any callable that accept *two* positional arguments: a
:term:`context` resource as the first argument and a :term:`request` as the
@@ -532,8 +528,7 @@ request
The following types work as view callables in this style:
-#. Functions that accept two arguments: ``context``, and ``request``,
- e.g.:
+#. Functions that accept two arguments: ``context`` and ``request``, e.g.:
.. code-block:: python
:linenos:
@@ -543,8 +538,8 @@ The following types work as view callables in this style:
def view(context, request):
return Response('OK')
-#. Classes that have an ``__init__`` method that accepts ``context,
- request`` and a ``__call__`` method which accepts no arguments, e.g.:
+#. Classes that have an ``__init__`` method that accepts ``context, request``,
+ and a ``__call__`` method which accepts no arguments, e.g.:
.. code-block:: python
:linenos:
@@ -559,8 +554,8 @@ The following types work as view callables in this style:
def __call__(self):
return Response('OK')
-#. Arbitrary callables that have a ``__call__`` method that accepts
- ``context, request``, e.g.:
+#. Arbitrary callables that have a ``__call__`` method that accepts ``context,
+ request``, e.g.:
.. code-block:: python
:linenos:
@@ -597,7 +592,6 @@ Pylons-1.0-Style "Controller" Dispatch
--------------------------------------
A package named :term:`pyramid_handlers` (available from PyPI) provides an
-analogue of :term:`Pylons` -style "controllers", which are a special kind of
-view class which provides more automation when your application uses
-:term:`URL dispatch` solely.
-
+analogue of :term:`Pylons`-style "controllers", which are a special kind of
+view class which provides more automation when your application uses :term:`URL
+dispatch` solely.
diff --git a/docs/narr/webob.rst b/docs/narr/webob.rst
index 0eb070b06..f18cf1dfb 100644
--- a/docs/narr/webob.rst
+++ b/docs/narr/webob.rst
@@ -13,26 +13,26 @@ Request and Response Objects
:app:`Pyramid` uses the :term:`WebOb` package as a basis for its
:term:`request` and :term:`response` object implementations. The
:term:`request` object that is passed to a :app:`Pyramid` :term:`view` is an
-instance of the :class:`pyramid.request.Request` class, which is a subclass
-of :class:`webob.Request`. The :term:`response` returned from a
-:app:`Pyramid` :term:`view` :term:`renderer` is an instance of the
+instance of the :class:`pyramid.request.Request` class, which is a subclass of
+:class:`webob.Request`. The :term:`response` returned from a :app:`Pyramid`
+:term:`view` :term:`renderer` is an instance of the
:mod:`pyramid.response.Response` class, which is a subclass of the
:class:`webob.Response` class. Users can also return an instance of
:class:`pyramid.response.Response` directly from a view as necessary.
-WebOb is a project separate from :app:`Pyramid` with a separate set of
-authors and a fully separate `set of documentation
-<http://docs.webob.org/en/latest/index.html>`_. Pyramid adds some
+WebOb is a project separate from :app:`Pyramid` with a separate set of authors
+and a fully separate `set of documentation
+<http://docs.webob.org/en/latest/index.html>`_. :app:`Pyramid` adds some
functionality to the standard WebOb request, which is documented in the
:ref:`request_module` API documentation.
WebOb provides objects for HTTP requests and responses. Specifically it does
-this by wrapping the `WSGI <http://wsgi.org>`_ request environment and
-response status, header list, and app_iter (body) values.
+this by wrapping the `WSGI <http://wsgi.org>`_ request environment and response
+status, header list, and app_iter (body) values.
WebOb request and response objects provide many conveniences for parsing WSGI
requests and forming WSGI responses. WebOb is a nice way to represent "raw"
-WSGI requests and responses; however, we won't cover that use case in this
+WSGI requests and responses. However, we won't cover that use case in this
document, as users of :app:`Pyramid` don't typically need to use the
WSGI-related features of WebOb directly. The `reference documentation
<http://docs.webob.org/en/latest/reference.html>`_ shows many examples of
@@ -47,64 +47,58 @@ Request
The request object is a wrapper around the `WSGI environ dictionary
<http://www.python.org/dev/peps/pep-0333/#environ-variables>`_. This
-dictionary contains keys for each header, keys that describe the
-request (including the path and query string), a file-like object for
-the request body, and a variety of custom keys. You can always access
-the environ with ``req.environ``.
+dictionary contains keys for each header, keys that describe the request
+(including the path and query string), a file-like object for the request body,
+and a variety of custom keys. You can always access the environ with
+``req.environ``.
-Some of the most important/interesting attributes of a request
-object:
+Some of the most important and interesting attributes of a request object are
+below.
-``req.method``:
- The request method, e.g., ``'GET'``, ``'POST'``
+``req.method``
+ The request method, e.g., ``GET``, ``POST``
-``req.GET``:
- A :term:`multidict` with all the variables in the query
- string.
+``req.GET``
+ A :term:`multidict` with all the variables in the query string.
-``req.POST``:
- A :term:`multidict` with all the variables in the request
- body. This only has variables if the request was a ``POST`` and
- it is a form submission.
+``req.POST``
+ A :term:`multidict` with all the variables in the request body. This only
+ has variables if the request was a ``POST`` and it is a form submission.
-``req.params``:
- A :term:`multidict` with a combination of everything in
- ``req.GET`` and ``req.POST``.
+``req.params``
+ A :term:`multidict` with a combination of everything in ``req.GET`` and
+ ``req.POST``.
-``req.body``:
- The contents of the body of the request. This contains the entire
- request body as a string. This is useful when the request is a
- ``POST`` that is *not* a form submission, or a request like a
- ``PUT``. You can also get ``req.body_file`` for a file-like
- object.
+``req.body``
+ The contents of the body of the request. This contains the entire request
+ body as a string. This is useful when the request is a ``POST`` that is
+ *not* a form submission, or a request like a ``PUT``. You can also get
+ ``req.body_file`` for a file-like object.
``req.json_body``
The JSON-decoded contents of the body of the request. See
:ref:`request_json_body`.
-``req.cookies``:
+``req.cookies``
A simple dictionary of all the cookies.
-``req.headers``:
+``req.headers``
A dictionary of all the headers. This dictionary is case-insensitive.
-``req.urlvars`` and ``req.urlargs``:
- ``req.urlvars`` are the keyword parameters associated with the
- request URL. ``req.urlargs`` are the positional parameters.
- These are set by products like `Routes
- <http://routes.groovie.org/>`_ and `Selector
- <http://lukearno.com/projects/selector/>`_.
+``req.urlvars`` and ``req.urlargs``
+ ``req.urlvars`` are the keyword parameters associated with the request URL.
+ ``req.urlargs`` are the positional parameters. These are set by products
+ like `Routes <http://routes.readthedocs.org/en/latest/>`_ and `Selector
+ <https://github.com/lukearno/selector>`_.
-Also, for standard HTTP request headers there are usually attributes,
-for instance: ``req.accept_language``, ``req.content_length``,
-``req.user_agent``, as an example. These properties expose the
-*parsed* form of each header, for whatever parsing makes sense. For
-instance, ``req.if_modified_since`` returns a `datetime
-<http://python.org/doc/current/lib/datetime-datetime.html>`_ object
-(or None if the header is was not provided).
+Also for standard HTTP request headers, there are usually attributes such as
+``req.accept_language``, ``req.content_length``, and ``req.user_agent``. These
+properties expose the *parsed* form of each header, for whatever parsing makes
+sense. For instance, ``req.if_modified_since`` returns a :mod:`datetime`
+object (or None if the header is was not provided).
-.. note:: Full API documentation for the :app:`Pyramid` request
- object is available in :ref:`request_module`.
+.. note:: Full API documentation for the :app:`Pyramid` request object is
+ available in :ref:`request_module`.
.. index::
single: request attributes (special)
@@ -112,14 +106,14 @@ instance, ``req.if_modified_since`` returns a `datetime
.. _special_request_attributes:
Special Attributes Added to the Request by :app:`Pyramid`
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
In addition to the standard :term:`WebOb` attributes, :app:`Pyramid` adds
special attributes to every request: ``context``, ``registry``, ``root``,
``subpath``, ``traversed``, ``view_name``, ``virtual_root``,
-``virtual_root_path``, ``session``, ``matchdict``, and ``matched_route``.
-These attributes are documented further within the
-:class:`pyramid.request.Request` API documentation.
+``virtual_root_path``, ``session``, ``matchdict``, and ``matched_route``. These
+attributes are documented further within the :class:`pyramid.request.Request`
+API documentation.
.. index::
single: request URLs
@@ -127,45 +121,43 @@ These attributes are documented further within the
URLs
++++
-In addition to these attributes, there are several ways to get the URL
-of the request. I'll show various values for an example URL
+In addition to these attributes, there are several ways to get the URL of the
+request and its parts. We'll show various values for an example URL
``http://localhost/app/blog?id=10``, where the application is mounted at
``http://localhost/app``.
-``req.url``:
- The full request URL, with query string, e.g.,
+``req.url``
+ The full request URL with query string, e.g.,
``http://localhost/app/blog?id=10``
-``req.host``:
- The host information in the URL, e.g.,
- ``localhost``
+``req.host``
+ The host information in the URL, e.g., ``localhost``
-``req.host_url``:
+``req.host_url``
The URL with the host, e.g., ``http://localhost``
-``req.application_url``:
- The URL of the application (just the SCRIPT_NAME portion of the
- path, not PATH_INFO). E.g., ``http://localhost/app``
+``req.application_url``
+ The URL of the application (just the ``SCRIPT_NAME`` portion of the path,
+ not ``PATH_INFO``), e.g., ``http://localhost/app``
-``req.path_url``:
- The URL of the application including the PATH_INFO. e.g.,
+``req.path_url``
+ The URL of the application including the ``PATH_INFO``, e.g.,
``http://localhost/app/blog``
-``req.path``:
- The URL including PATH_INFO without the host or scheme. e.g.,
+``req.path``
+ The URL including ``PATH_INFO`` without the host or scheme, e.g.,
``/app/blog``
-``req.path_qs``:
- The URL including PATH_INFO and the query string. e.g,
+``req.path_qs``
+ The URL including ``PATH_INFO`` and the query string, e.g,
``/app/blog?id=10``
-``req.query_string``:
- The query string in the URL, e.g.,
- ``id=10``
+``req.query_string``
+ The query string in the URL, e.g., ``id=10``
-``req.relative_url(url, to_application=False)``:
- Gives a URL, relative to the current URL. If ``to_application``
- is True, then resolves it relative to ``req.application_url``.
+``req.relative_url(url, to_application=False)``
+ Gives a URL relative to the current URL. If ``to_application`` is True,
+ then resolves it relative to ``req.application_url``.
.. index::
single: request methods
@@ -177,39 +169,34 @@ There are methods of request objects documented in
:class:`pyramid.request.Request` but you'll find that you won't use very many
of them. Here are a couple that might be useful:
-``Request.blank(base_url)``:
- Creates a new request with blank information, based at the given
- URL. This can be useful for subrequests and artificial requests.
- You can also use ``req.copy()`` to copy an existing request, or
- for subrequests ``req.copy_get()`` which copies the request but
- always turns it into a GET (which is safer to share for
- subrequests).
+``Request.blank(base_url)``
+ Creates a new request with blank information, based at the given URL. This
+ can be useful for subrequests and artificial requests. You can also use
+ ``req.copy()`` to copy an existing request, or for subrequests
+ ``req.copy_get()`` which copies the request but always turns it into a GET
+ (which is safer to share for subrequests).
-``req.get_response(wsgi_application)``:
- This method calls the given WSGI application with this request, and
- returns a :class:`pyramid.response.Response` object. You can also use
- this for subrequests, or testing.
+``req.get_response(wsgi_application)``
+ This method calls the given WSGI application with this request, and returns
+ a :class:`pyramid.response.Response` object. You can also use this for
+ subrequests or testing.
.. index::
- single: request (and unicode)
- single: unicode (and the request)
+ single: request (and text/unicode)
+ single: unicode and text (and the request)
-Unicode
-+++++++
+Text (Unicode)
+++++++++++++++
-Many of the properties in the request object will return unicode
-values if the request encoding/charset is provided. The client *can*
+Many of the properties of the request object will be text values (``unicode``
+under Python 2 or ``str`` under Python 3) if the request encoding/charset is
+provided. If it is provided, the values in ``req.POST``, ``req.GET``,
+``req.params``, and ``req.cookies`` will contain text. The client *can*
indicate the charset with something like ``Content-Type:
-application/x-www-form-urlencoded; charset=utf8``, but browsers seldom
-set this. You can set the charset with ``req.charset = 'utf8'``, or
-during instantiation with ``Request(environ, charset='utf8')``. If
-you subclass ``Request`` you can also set ``charset`` as a class-level
-attribute.
-
-If it is set, then ``req.POST``, ``req.GET``, ``req.params``, and
-``req.cookies`` will contain unicode strings. Each has a
-corresponding ``req.str_*`` (e.g., ``req.str_POST``) that is always
-a ``str``, and never unicode.
+application/x-www-form-urlencoded; charset=utf8``, but browsers seldom set
+this. You can reset the charset of an existing request with ``newreq =
+req.decode('utf-8')``, or during instantiation with ``Request(environ,
+charset='utf8')``.
.. index::
single: multidict (WebOb)
@@ -219,26 +206,25 @@ a ``str``, and never unicode.
Multidict
+++++++++
-Several attributes of a WebOb request are "multidict"; structures (such as
+Several attributes of a WebOb request are multidict structures (such as
``request.GET``, ``request.POST``, and ``request.params``). A multidict is a
-dictionary where a key can have multiple values. The quintessential example
-is a query string like ``?pref=red&pref=blue``; the ``pref`` variable has two
+dictionary where a key can have multiple values. The quintessential example is
+a query string like ``?pref=red&pref=blue``; the ``pref`` variable has two
values: ``red`` and ``blue``.
-In a multidict, when you do ``request.GET['pref']`` you'll get back
-only ``'blue'`` (the last value of ``pref``). Sometimes returning a
-string, and sometimes returning a list, is the cause of frequent
-exceptions. If you want *all* the values back, use
-``request.GET.getall('pref')``. If you want to be sure there is *one
-and only one* value, use ``request.GET.getone('pref')``, which will
-raise an exception if there is zero or more than one value for
-``pref``.
-
-When you use operations like ``request.GET.items()`` you'll get back
-something like ``[('pref', 'red'), ('pref', 'blue')]``. All the
-key/value pairs will show up. Similarly ``request.GET.keys()``
-returns ``['pref', 'pref']``. Multidict is a view on a list of
-tuples; all the keys are ordered, and all the values are ordered.
+In a multidict, when you do ``request.GET['pref']``, you'll get back only
+``"blue"`` (the last value of ``pref``). This returned result might not be
+expected—sometimes returning a string, and sometimes returning a list—and may
+be cause of frequent exceptions. If you want *all* the values back, use
+``request.GET.getall('pref')``. If you want to be sure there is *one and only
+one* value, use ``request.GET.getone('pref')``, which will raise an exception
+if there is zero or more than one value for ``pref``.
+
+When you use operations like ``request.GET.items()``, you'll get back something
+like ``[('pref', 'red'), ('pref', 'blue')]``. All the key/value pairs will
+show up. Similarly ``request.GET.keys()`` returns ``['pref', 'pref']``.
+Multidict is a view on a list of tuples; all the keys are ordered, and all the
+values are ordered.
API documentation for a multidict exists as
:class:`pyramid.interfaces.IMultiDict`.
@@ -248,19 +234,19 @@ API documentation for a multidict exists as
.. _request_json_body:
-Dealing With A JSON-Encoded Request Body
+Dealing with a JSON-Encoded Request Body
++++++++++++++++++++++++++++++++++++++++
.. versionadded:: 1.1
:attr:`pyramid.request.Request.json_body` is a property that returns a
-:term:`JSON` -decoded representation of the request body. If the request
-does not have a body, or the body is not a properly JSON-encoded value, an
-exception will be raised when this attribute is accessed.
+:term:`JSON`-decoded representation of the request body. If the request does
+not have a body, or the body is not a properly JSON-encoded value, an exception
+will be raised when this attribute is accessed.
-This attribute is useful when you invoke a Pyramid view callable via
-e.g. jQuery's ``$.ajax`` function, which has the potential to send a request
-with a JSON-encoded body.
+This attribute is useful when you invoke a :app:`Pyramid` view callable via,
+for example, jQuery's ``$.ajax`` function, which has the potential to send a
+request with a JSON-encoded body.
Using ``request.json_body`` is equivalent to:
@@ -269,15 +255,15 @@ Using ``request.json_body`` is equivalent to:
from json import loads
loads(request.body, encoding=request.charset)
-Here's how to construct an AJAX request in Javascript using :term:`jQuery`
-that allows you to use the ``request.json_body`` attribute when the request
-is sent to a Pyramid application:
+Here's how to construct an AJAX request in JavaScript using :term:`jQuery` that
+allows you to use the ``request.json_body`` attribute when the request is sent
+to a :app:`Pyramid` application:
.. code-block:: javascript
- jQuery.ajax({type:'POST',
+ jQuery.ajax({type:'POST',
url: 'http://localhost:6543/', // the pyramid server
- data: JSON.stringify({'a':1}),
+ data: JSON.stringify({'a':1}),
contentType: 'application/json; charset=utf-8'});
When such a request reaches a view in your application, the
@@ -296,14 +282,14 @@ For the above view, printed to the console will be:
{u'a': 1}
-For bonus points, here's a bit of client-side code that will produce a
-request that has a body suitable for reading via ``request.json_body`` using
-Python's ``urllib2`` instead of a Javascript AJAX request:
+For bonus points, here's a bit of client-side code that will produce a request
+that has a body suitable for reading via ``request.json_body`` using Python's
+``urllib2`` instead of a JavaScript AJAX request:
.. code-block:: python
import urllib2
- import json
+ import json
json_payload = json.dumps({'a':1})
headers = {'Content-Type':'application/json; charset=utf-8'}
@@ -312,22 +298,22 @@ Python's ``urllib2`` instead of a Javascript AJAX request:
If you are doing Cross-origin resource sharing (CORS), then the standard
requires the browser to do a pre-flight HTTP OPTIONS request. The easiest way
-to handling this is adding an extra ``view_config`` for the same route, with
-``request_method`` set to ``OPTIONS``, and setting the desired response header
-before returning. You can find examples of response headers here_.
-
-.. _here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
+to handle this is to add an extra ``view_config`` for the same route, with
+``request_method`` set to ``OPTIONS``, and set the desired response header
+before returning. You can find examples of response headers `Access control
+CORS, Preflighted requests
+<https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests>`_.
.. index::
single: cleaning up after request
.. _cleaning_up_after_a_request:
-Cleaning Up After a Request
+Cleaning up after a Request
+++++++++++++++++++++++++++
-Sometimes it's required that some cleanup be performed at the end of a
-request when a database connection is involved.
+Sometimes it's required to perform some cleanup at the end of a request when a
+database connection is involved.
For example, let's say you have a ``mypackage`` :app:`Pyramid` application
package that uses SQLAlchemy, and you'd like the current SQLAlchemy database
@@ -352,29 +338,28 @@ session to be removed after each request. Put the following in the
Registering the ``cleanup_callback`` finished callback at the start of a
request (by causing the ``add_cleanup_callback`` to receive a
:class:`pyramid.events.NewRequest` event at the start of each request) will
-cause the DBSession to be removed whenever request processing has ended.
-Note that in the example above, for the :class:`pyramid.events.subscriber`
-decorator to "work", the :meth:`pyramid.config.Configurator.scan` method must
-be called against your ``mypackage`` package during application
-initialization.
-
-.. note:: This is only an example. In particular, it is not necessary to
- cause ``DBSession.remove`` to be called in an application generated from
- any :app:`Pyramid` scaffold, because these all use the ``pyramid_tm``
- package. The cleanup done by ``DBSession.remove`` is unnecessary when
- ``pyramid_tm`` :term:`middleware` is configured into the application.
+cause the DBSession to be removed whenever request processing has ended. Note
+that in the example above, for the :class:`pyramid.events.subscriber` decorator
+to work, the :meth:`pyramid.config.Configurator.scan` method must be called
+against your ``mypackage`` package during application initialization.
+
+.. note::
+ This is only an example. In particular, it is not necessary to cause
+ ``DBSession.remove`` to be called in an application generated from any
+ :app:`Pyramid` scaffold, because these all use the ``pyramid_tm`` package.
+ The cleanup done by ``DBSession.remove`` is unnecessary when ``pyramid_tm``
+ :term:`middleware` is configured into the application.
More Details
++++++++++++
-More detail about the request object API is available in:
+More detail about the request object API is available as follows.
-- The :class:`pyramid.request.Request` API documentation.
+- :class:`pyramid.request.Request` API documentation
-- The `WebOb documentation <http://docs.webob.org/en/latest/index.html>`_.
- All methods and attributes of a ``webob.Request`` documented within the
- WebOb documentation will work with request objects created by
- :app:`Pyramid`.
+- `WebOb documentation <http://docs.webob.org/en/latest/index.html>`_. All
+ methods and attributes of a ``webob.Request`` documented within the WebOb
+ documentation will work with request objects created by :app:`Pyramid`.
.. index::
single: response object
@@ -385,67 +370,62 @@ Response
The :app:`Pyramid` response object can be imported as
:class:`pyramid.response.Response`. This class is a subclass of the
``webob.Response`` class. The subclass does not add or change any
-functionality, so the WebOb Response documentation will be completely
-relevant for this class as well.
+functionality, so the WebOb Response documentation will be completely relevant
+for this class as well.
A response object has three fundamental parts:
-``response.status``:
- The response code plus reason message, like ``'200 OK'``. To set
- the code without a message, use ``status_int``, i.e.:
- ``response.status_int = 200``.
-
-``response.headerlist``:
- A list of all the headers, like ``[('Content-Type',
- 'text/html')]``. There's a case-insensitive :term:`multidict`
- in ``response.headers`` that also allows you to access
- these same headers.
-
-``response.app_iter``:
- An iterable (such as a list or generator) that will produce the
- content of the response. This is also accessible as
- ``response.body`` (a string), ``response.unicode_body`` (a
- unicode object, informed by ``response.charset``), and
- ``response.body_file`` (a file-like object; writing to it appends
- to ``app_iter``).
+``response.status``
+ The response code plus reason message, like ``200 OK``. To set the code
+ without a message, use ``status_int``, i.e., ``response.status_int = 200``.
+
+``response.headerlist``
+ A list of all the headers, like ``[('Content-Type', 'text/html')]``.
+ There's a case-insensitive :term:`multidict` in ``response.headers`` that
+ also allows you to access these same headers.
+
+``response.app_iter``
+ An iterable (such as a list or generator) that will produce the content of
+ the response. This is also accessible as ``response.body`` (a string),
+ ``response.text`` (a unicode object, informed by ``response.charset``), and
+ ``response.body_file`` (a file-like object; writing to it appends to
+ ``app_iter``).
Everything else in the object typically derives from this underlying state.
Here are some highlights:
``response.content_type``
The content type *not* including the ``charset`` parameter.
+
Typical use: ``response.content_type = 'text/html'``.
Default value: ``response.content_type = 'text/html'``.
-``response.charset``:
- The ``charset`` parameter of the content-type, it also informs
- encoding in ``response.unicode_body``.
- ``response.content_type_params`` is a dictionary of all the
- parameters.
-
-``response.set_cookie(key, value, max_age=None, path='/', ...)``:
- Set a cookie. The keyword arguments control the various cookie
- parameters. The ``max_age`` argument is the length for the cookie
- to live in seconds (you may also use a timedelta object). The
- ``Expires`` key will also be set based on the value of
- ``max_age``.
-
-``response.delete_cookie(key, path='/', domain=None)``:
- Delete a cookie from the client. This sets ``max_age`` to 0 and
- the cookie value to ``''``.
-
-``response.cache_expires(seconds=0)``:
- This makes this response cacheable for the given number of seconds,
- or if ``seconds`` is 0 then the response is uncacheable (this also
- sets the ``Expires`` header).
-
-``response(environ, start_response)``:
- The response object is a WSGI application. As an application, it
- acts according to how you create it. It *can* do conditional
- responses if you pass ``conditional_response=True`` when
- instantiating (or set that attribute later). It can also do HEAD
- and Range requests.
+``response.charset``
+ The ``charset`` parameter of the content-type, it also informs encoding in
+ ``response.text``. ``response.content_type_params`` is a dictionary of all
+ the parameters.
+
+``response.set_cookie(key, value, max_age=None, path='/', ...)``
+ Set a cookie. The keyword arguments control the various cookie parameters.
+ The ``max_age`` argument is the length for the cookie to live in seconds
+ (you may also use a timedelta object). The ``Expires`` key will also be
+ set based on the value of ``max_age``.
+
+``response.delete_cookie(key, path='/', domain=None)``
+ Delete a cookie from the client. This sets ``max_age`` to 0 and the cookie
+ value to ``''``.
+
+``response.cache_expires(seconds=0)``
+ This makes the response cacheable for the given number of seconds, or if
+ ``seconds`` is ``0`` then the response is uncacheable (this also sets the
+ ``Expires`` header).
+
+``response(environ, start_response)``
+ The response object is a WSGI application. As an application, it acts
+ according to how you create it. It *can* do conditional responses if you
+ pass ``conditional_response=True`` when instantiating (or set that
+ attribute later). It can also do HEAD and Range requests.
.. index::
single: response headers
@@ -453,12 +433,11 @@ Here are some highlights:
Headers
+++++++
-Like the request, most HTTP response headers are available as
-properties. These are parsed, so you can do things like
-``response.last_modified = os.path.getmtime(filename)``.
+Like the request, most HTTP response headers are available as properties. These
+are parsed, so you can do things like ``response.last_modified =
+os.path.getmtime(filename)``.
-The details are available in the `extracted Response documentation
-<http://docs.webob.org/en/latest/modules/webob.html#headers>`_.
+The details are available in the :mod:`webob.response` API documentation.
.. index::
single: response (creating)
@@ -466,9 +445,9 @@ The details are available in the `extracted Response documentation
Instantiating the Response
++++++++++++++++++++++++++
-Of course most of the time you just want to *make* a response.
-Generally any attribute of the response can be passed in as a keyword
-argument to the class; e.g.:
+Of course most of the time you just want to *make* a response. Generally any
+attribute of the response can be passed in as a keyword argument to the class,
+e.g.:
.. code-block:: python
:linenos:
@@ -478,9 +457,9 @@ argument to the class; e.g.:
The status defaults to ``'200 OK'``.
-The value of content_type defaults to
-``webob.response.Response.default_content_type``; which is `text/html`.
-You can subclass :class:`pyramid.response.Response` and set
+The value of ``content_type`` defaults to
+``webob.response.Response.default_content_type``, which is ``text/html``. You
+can subclass :class:`pyramid.response.Response` and set
``default_content_type`` to override this behavior.
.. index::
@@ -490,18 +469,16 @@ Exception Responses
+++++++++++++++++++
To facilitate error responses like ``404 Not Found``, the module
-:mod:`pyramid.httpexceptions` contains classes for each kind of error
-response. These include boring, but appropriate error bodies. The
-exceptions exposed by this module, when used under :app:`Pyramid`, should be
-imported from the :mod:`pyramid.httpexceptions` module. This import location
-contains subclasses and replacements that mirror those in the ``webob.exc``
-module.
-
-Each class is named ``pyramid.httpexceptions.HTTP*``, where ``*`` is the
-reason for the error. For instance,
-:class:`pyramid.httpexceptions.HTTPNotFound` subclasses
-:class:`pyramid.response.Response`, so you can manipulate the instances in the same
-way. A typical example is:
+:mod:`pyramid.httpexceptions` contains classes for each kind of error response.
+These include boring but appropriate error bodies. The exceptions exposed by
+this module, when used under :app:`Pyramid`, should be imported from the
+:mod:`pyramid.httpexceptions` module. This import location contains subclasses
+and replacements that mirror those in the ``webob.exc`` module.
+
+Each class is named ``pyramid.httpexceptions.HTTP*``, where ``*`` is the reason
+for the error. For instance, :class:`pyramid.httpexceptions.HTTPNotFound`
+subclasses :class:`pyramid.response.Response`, so you can manipulate the
+instances in the same way. A typical example is:
.. code-block:: python
:linenos:
@@ -517,8 +494,6 @@ More Details
++++++++++++
More details about the response object API are available in the
-:mod:`pyramid.response` documentation. More details about exception
-responses are in the :mod:`pyramid.httpexceptions` API documentation. The
-`WebOb documentation <http://docs.webob.org/en/latest/index.html>`_ is also
-useful.
-
+:mod:`pyramid.response` documentation. More details about exception responses
+are in the :mod:`pyramid.httpexceptions` API documentation. The `WebOb
+documentation <http://docs.webob.org/en/latest/index.html>`_ is also useful.
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst
index 41a0dc8c0..be5be2e36 100644
--- a/docs/quick_tour.rst
+++ b/docs/quick_tour.rst
@@ -14,11 +14,10 @@ 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/>`_.
+For this Quick Tour, it means: `Python <https://www.python.org/downloads/>`_,
+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:
@@ -40,14 +39,18 @@ For Windows:
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.)
+production support for Python 3 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.
+ Why ``easy_install`` and not ``pip``? Some distributions on which Pyramid
+ depends upon have optional C extensions for performance. ``pip`` cannot
+ install some binary Python distributions. With ``easy_install``, Windows
+ users are able to obtain binary Python distributions, so they get the
+ benefit of the C extensions without needing a C compiler. Also, there can
+ be issues when ``pip`` and ``easy_install`` are used side-by-side in the
+ same environment, so we chose to recommend ``easy_install`` for the sake of
+ reducing the complexity of these instructions.
.. seealso:: See also:
:ref:`Quick Tutorial section on Requirements <qtut_requirements>`,
@@ -70,8 +73,8 @@ This simple example is easy to run. Save this as ``app.py`` and run it:
$ python ./app.py
-Next, open `http://localhost:6543/ <http://localhost:6543/>`_ in a
-browser and you will see the ``Hello World!`` message.
+Next open http://localhost:6543/ in a browser, and you will see the ``Hello
+World!`` message.
New to Python web programming? If so, some lines in the module merit
explanation:
@@ -97,7 +100,7 @@ one that we will revisit regurlarly in this *Quick Tour*.
:ref:`firstapp_chapter`, and
:ref:`Single File Tasks tutorial <tutorials:single-file-tutorial>`
-Handling Web Requests and Responses
+Handling web requests and responses
===================================
Developing for the web means processing web requests. As this is a
@@ -105,21 +108,20 @@ 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>`.
+development (virtual environments, packaging, scaffolding, one of the first to
+embrace Python 3, etc.). 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::
+In this Pyramid view, we get the URL being visited from ``request.url``. Also,
+if you visited http://localhost:6543/?name=alice in a browser, the name is
+included in the body of the response::
URL http://localhost:6543/?name=alice with name: alice
@@ -143,13 +145,15 @@ So far our examples place everything in one file:
- its registration with the configurator
-- the route to map it to a URL
+- the route to map it to an 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 set up
-the views. First, our revised ``app.py``:
+the views.
+
+First, our revised ``app.py``:
.. literalinclude:: quick_tour/views/app.py
:linenos:
@@ -164,8 +168,8 @@ 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
+We have four 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 issues a redirect to the final
view.
@@ -176,7 +180,7 @@ section introduces ``@view_config``. Pyramid's configuration supports
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.
+configuration, thus usually it is simply a matter of taste.
.. seealso:: See also:
:ref:`Quick Tutorial Views <qtut_views>`,
@@ -196,7 +200,7 @@ 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
+- Elsewhere a view is configured to be called for that route name
.. note::
@@ -283,12 +287,12 @@ we can use ``name`` as a variable in our template via
:ref:`debugging_templates`, and
:ref:`available_template_system_bindings`
-Templating With ``jinja2``
+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``,
+modeled 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:
@@ -325,12 +329,12 @@ renderer.
`Jinja2 homepage <http://jinja.pocoo.org/>`_, and
:ref:`pyramid_jinja2 Overview <jinja2:overview>`
-Static Assets
+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
+Pyramid will serve some static assets. First another call to the
:term:`configurator`:
.. literalinclude:: quick_tour/static_assets/app.py
@@ -338,10 +342,10 @@ Pyramid will serve some static assets. First, another call to the
:end-before: End Static 1
This tells our WSGI application to map requests under
-``http://localhost:6543/static/`` to files and directories inside a
+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:
+Next make a directory named ``static``, and place ``app.css`` inside:
.. literalinclude:: quick_tour/static_assets/static/app.css
:language: css
@@ -395,14 +399,13 @@ into JSON and set the appropriate HTTP headers.
:ref:`json_renderer`, and
:ref:`adding_and_overriding_renderers`
-View Classes
+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:
+data, or a REST API that handles multiple operations. Grouping these
+together as a :ref:`view class <class_as_view>` makes sense.
- Group views
@@ -426,14 +429,14 @@ Specifically:
- 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
+ ``<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"/>``.
+ 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
+Only one route is needed, stated in one place atop the view class. Also,
+the assignment of ``name`` is done in the ``__init__`` function. Our
templates can then use ``{{ view.name }}``.
Pyramid view classes, combined with built-in and custom predicates,
@@ -451,7 +454,7 @@ have much more to offer:
:ref:`Quick Tutorial More View Classes <qtut_more_view_classes>`, and
:ref:`class_as_view`
-Quick Project Startup with Scaffolds
+Quick project startup with scaffolds
====================================
So far we have done all of our *Quick Tour* as a single Python file.
@@ -502,10 +505,10 @@ Let's look at ``pserve`` and configuration in more depth.
:ref:`project_narr`, and
:doc:`../narr/scaffolding`
-Application Running with ``pserve``
+Application running with ``pserve``
===================================
-Prior to scaffolds, our project mixed a number of operations details
+Prior to scaffolds, our project mixed a number of operational details
into our code. Why should my main code care which HTTP server I want and
what port number to run on?
@@ -531,19 +534,19 @@ take a look at this configuration file.
.. seealso:: See also:
:ref:`what_is_this_pserve_thing`
-Configuration with ``.ini`` Files
+Configuration with ``.ini`` files
=================================
Earlier in *Quick Tour* 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
+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
+Let's take a quick high-level look. First the ``.ini`` file is divided
into sections:
- ``[app:hello_world]`` configures our WSGI app
@@ -556,23 +559,21 @@ into sections:
We have a few decisions made for us in this configuration:
-#. *Choice of web server*. The ``use = egg:pyramid#wsgiref`` tells
- ``pserve`` to use the ``wsgiref`` server that is wrapped in the Pyramid
- package.
+#. *Choice of web server:* ``use = egg:pyramid#wsgiref`` tells ``pserve`` to
+ use the ``wsgiref`` server that is wrapped in the Pyramid package.
-#. *Port number*. ``port = 6543`` tells ``wsgiref`` to listen on port
- 6543.
+#. *Port number:* ``port = 6543`` tells ``wsgiref`` to listen on port 6543.
-#. *WSGI app*. What package has our WSGI application in it?
+#. *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.
+#. *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
+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 as traceback information.
@@ -582,7 +583,7 @@ a log on every request that comes in, as well as traceback information.
:doc:`../narr/paste`
-Easier Development with ``debugtoolbar``
+Easier development with ``debugtoolbar``
========================================
As we introduce the basics, we also want to show how to be productive in
@@ -593,21 +594,21 @@ reloading and earlier we showed ``--reload`` for application reloading.
several tools available in your browser. Adding it to your project
illustrates several points about configuration.
-First, change your ``setup.py`` to say:
+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:
+...and rerun 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
+The Python package ``pyramid_debugtoolbar`` is 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
@@ -627,7 +628,7 @@ 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
+you want to disable this toolbar, there's no need to change code: you can
remove it from ``pyramid.includes`` in the relevant ``.ini``
configuration file.
@@ -636,12 +637,12 @@ configuration file.
pyramid_debugtoolbar <qtut_debugtoolbar>` and
:ref:`pyramid_debugtoolbar <toolbar:overview>`
-Unit Tests and ``nose``
+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.
+Yikes! We got this far and we haven't yet discussed tests. This is
+particularly egregious, as Pyramid has had a deep commitment to full test
+coverage since before its release.
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``
@@ -657,7 +658,7 @@ the ``coverage`` tool which yells at us for code that isn't tested:
}
)
-We changed ``setup.py`` which means we need to re-run
+We changed ``setup.py`` which means we need to rerun
``python ./setup.py develop``. We can now run all our tests:
.. code-block:: bash
@@ -695,7 +696,7 @@ 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,
+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*.
@@ -717,7 +718,7 @@ You can now, in your code, log messages:
:start-after: Start Logging 2
:end-before: End Logging 2
-This will log ``Some Message`` at a ``debug`` log level,
+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:
@@ -728,7 +729,7 @@ controls that? These sections in the configuration file:
Our application, a package named ``hello_world``, is set up 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::
+visit http://localhost:6543, your console will now show::
2013-08-09 10:42:42,968 DEBUG [hello_world.views][MainThread] Some Message
@@ -887,8 +888,8 @@ 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
+Also the ``deform_bootstrap`` Pyramid add-on restyles the stock Deform
+widgets using attractive CSS from Twitter Bootstrap and more powerful widgets
from Chosen.
.. seealso:: See also:
diff --git a/docs/quick_tutorial/authentication.rst b/docs/quick_tutorial/authentication.rst
index 4b4eb1ba3..7fd8173d4 100644
--- a/docs/quick_tutorial/authentication.rst
+++ b/docs/quick_tutorial/authentication.rst
@@ -93,7 +93,7 @@ Steps
Analysis
========
-Unlike many web frameworks, Pyramid includes a built-in (but optional)
+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
@@ -123,9 +123,6 @@ Extra Credit
#. 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.
diff --git a/docs/quick_tutorial/authentication/tutorial/home.pt b/docs/quick_tutorial/authentication/tutorial/home.pt
index 6ecd0081b..ed911b673 100644
--- a/docs/quick_tutorial/authentication/tutorial/home.pt
+++ b/docs/quick_tutorial/authentication/tutorial/home.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
@@ -15,4 +15,4 @@
<h1>Hi ${name}</h1>
<p>Visit <a href="${request.route_url('hello')}">hello</a></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authentication/tutorial/login.pt b/docs/quick_tutorial/authentication/tutorial/login.pt
index 4451fc4f8..9e5bfe2ad 100644
--- a/docs/quick_tutorial/authentication/tutorial/login.pt
+++ b/docs/quick_tutorial/authentication/tutorial/login.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Login</h1>
@@ -22,4 +22,4 @@
value="Log In"/>
</form>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authorization.rst b/docs/quick_tutorial/authorization.rst
index 6b10d3409..855043f7f 100644
--- a/docs/quick_tutorial/authorization.rst
+++ b/docs/quick_tutorial/authorization.rst
@@ -11,7 +11,7 @@ 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
+``editor`` and password of ``editor``). We will allow the other views
to continue working without a password.
Objectives
@@ -93,7 +93,7 @@ In summary: ``hello`` wants ``edit`` permission, ``Root`` says
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
+If you are not logged in and visit ``/howdy``, 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``.
@@ -101,7 +101,10 @@ by decorating the view with ``@forbidden_view_config``.
Extra Credit
============
-#. Perhaps you would like experience of not having enough permissions
+#. Do I have to put a ``renderer`` in my ``@forbidden_view_config``
+ decorator?
+
+#. Perhaps you would like the 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
diff --git a/docs/quick_tutorial/authorization/tutorial/home.pt b/docs/quick_tutorial/authorization/tutorial/home.pt
index 6ecd0081b..ed911b673 100644
--- a/docs/quick_tutorial/authorization/tutorial/home.pt
+++ b/docs/quick_tutorial/authorization/tutorial/home.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
@@ -15,4 +15,4 @@
<h1>Hi ${name}</h1>
<p>Visit <a href="${request.route_url('hello')}">hello</a></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authorization/tutorial/login.pt b/docs/quick_tutorial/authorization/tutorial/login.pt
index 4451fc4f8..9e5bfe2ad 100644
--- a/docs/quick_tutorial/authorization/tutorial/login.pt
+++ b/docs/quick_tutorial/authorization/tutorial/login.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Login</h1>
@@ -22,4 +22,4 @@
value="Log In"/>
</form>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/databases.rst b/docs/quick_tutorial/databases.rst
index 7c019dbfc..19dfd066d 100644
--- a/docs/quick_tutorial/databases.rst
+++ b/docs/quick_tutorial/databases.rst
@@ -53,8 +53,8 @@ Steps
.. note::
- We aren't yet doing ``python3.3 setup.py develop`` as we
- are changing it later.
+ We aren't yet doing ``$VENV/bin/python setup.py develop`` as we
+ will change it later.
#. Our configuration file at ``databases/development.ini`` wires
together some new pieces:
@@ -72,6 +72,7 @@ Steps
to initialize the database:
.. literalinclude:: databases/tutorial/initialize_db.py
+ :linenos:
#. Since ``setup.py`` changed, we now run it:
@@ -89,21 +90,34 @@ Steps
.. code-block:: bash
$ $VENV/bin/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]
+ 2015-06-01 11:22:52,650 INFO [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
+ 2015-06-01 11:22:52,650 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2015-06-01 11:22:52,651 INFO [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
+ 2015-06-01 11:22:52,651 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2015-06-01 11:22:52,652 INFO [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages")
+ 2015-06-01 11:22:52,652 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2015-06-01 11:22:52,653 INFO [sqlalchemy.engine.base.Engine][MainThread]
CREATE TABLE wikipages (
- uid INTEGER NOT NULL,
- title TEXT,
- body TEXT,
- PRIMARY KEY (uid),
- UNIQUE (title)
+ uid INTEGER NOT NULL,
+ title TEXT,
+ body TEXT,
+ PRIMARY KEY (uid),
+ UNIQUE (title)
)
+
+ 2015-06-01 11:22:52,653 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2015-06-01 11:22:52,655 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT
+ 2015-06-01 11:22:52,658 INFO [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit)
+ 2015-06-01 11:22:52,659 INFO [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO wikipages (title, body) VALUES (?, ?)
+ 2015-06-01 11:22:52,659 INFO [sqlalchemy.engine.base.Engine][MainThread] ('Root', '<p>Root</p>')
+ 2015-06-01 11:22:52,659 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT
+
#. With our data now driven by SQLAlchemy queries, we need to update
our ``databases/tutorial/views.py``:
.. literalinclude:: databases/tutorial/views.py
+ :linenos:
#. Our tests in ``databases/tutorial/tests.py`` changed to include
SQLAlchemy bootstrapping:
@@ -138,8 +152,8 @@ Let's start with the dependencies. We made the decision to use
``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,
+Specifically, you can install a transaction manager into your
+application either as middleware or a Pyramid "tween". Then,
just before you return the response, all transaction-aware parts of
your application are executed.
@@ -149,7 +163,7 @@ 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
+a package that makes SQLAlchemy, and thus the RDBMS transaction manager,
integrate with the Pyramid transaction manager. That's what
``zope.sqlalchemy`` does.
@@ -167,8 +181,8 @@ 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.)
+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
diff --git a/docs/quick_tutorial/databases/development.ini b/docs/quick_tutorial/databases/development.ini
index 04c249a62..5da87d602 100644
--- a/docs/quick_tutorial/databases/development.ini
+++ b/docs/quick_tutorial/databases/development.ini
@@ -11,3 +11,39 @@ sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite
use = egg:pyramid#wsgiref
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial, sqlalchemy.engine.base.Engine
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy.engine.base.Engine]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine.base.Engine
+
+[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/tutorial/tests.py b/docs/quick_tutorial/databases/tutorial/tests.py
index e18e70c8c..11e747d15 100644
--- a/docs/quick_tutorial/databases/tutorial/tests.py
+++ b/docs/quick_tutorial/databases/tutorial/tests.py
@@ -40,16 +40,14 @@ class WikiViewTests(unittest.TestCase):
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()
+ from .models import DBSession
+ DBSession.remove()
def test_it(self):
res = self.testapp.get('/', status=200)
diff --git a/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
index d1fea0d7f..01955ef72 100644
--- a/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
+++ b/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
@@ -4,10 +4,12 @@
<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)}"/>
+ href="${request.static_url(reqt)}">
</tal:block>
+ <script type="text/javascript"
+ src="${request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}"></script>
<tal:block tal:repeat="reqt view.reqts['js']">
- <script src="${request.static_url('deform:static/' + reqt)}"
+ <script src="${request.static_url(reqt)}"
type="text/javascript"></script>
</tal:block>
</head>
diff --git a/docs/quick_tutorial/debugtoolbar.rst b/docs/quick_tutorial/debugtoolbar.rst
index d138eb760..f11abc493 100644
--- a/docs/quick_tutorial/debugtoolbar.rst
+++ b/docs/quick_tutorial/debugtoolbar.rst
@@ -74,11 +74,11 @@ You'll now see an attractive button on the right side of your browser, which
you may click to provide introspective access to debugging information in a
new browser tab. 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.)
+toolbar, there's 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 injects a small amount of html/css into your app just
+Note that the toolbar injects a small amount of HTML/CSS into your app just
before the closing ``</body>`` tag in order to display itself. If you start to
experience otherwise inexplicable client-side weirdness, you can shut it off
by commenting out the ``pyramid_debugtoolbar`` line in ``pyramid.includes``
@@ -89,24 +89,24 @@ temporarily.
Extra Credit
============
-# Why don't we add ``pyramid_debugtoolbar`` to the list of
- ``install_requires`` dependencies in ``debugtoolbar/setup.py``?
+#. Why don't we add ``pyramid_debugtoolbar`` to the list of
+ ``install_requires`` dependencies in ``debugtoolbar/setup.py``?
-# Introduce a bug into your application: Change:
+#. Introduce a bug into your application: Change:
- .. code-block:: python
+ .. code-block:: python
- def hello_world(request):
- return Response('<body><h1>Hello World!</h1></body>')
+ def hello_world(request):
+ return Response('<body><h1>Hello World!</h1></body>')
- to:
+ to:
- .. code-block:: python
+ .. code-block:: python
def hello_world(request):
return xResponse('<body><h1>Hello World!</h1></body>')
- Save, and visit http://localhost:6543/ again. Notice the nice
- traceback display. On the lowest line, click the "screen" icon to the
- right, and try typing the variable names ``request`` and ``Response``.
- What else can you discover?
+ Save, and visit http://localhost:6543/ again. Notice the nice
+ traceback display. On the lowest line, click the "screen" icon to the
+ right, and try typing the variable names ``request`` and ``Response``.
+ What else can you discover?
diff --git a/docs/quick_tutorial/forms.rst b/docs/quick_tutorial/forms.rst
index b08167edc..f81b88fc2 100644
--- a/docs/quick_tutorial/forms.rst
+++ b/docs/quick_tutorial/forms.rst
@@ -12,13 +12,13 @@ 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
+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.
+forms and validation. This also gives us :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
diff --git a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
index 3292dfd90..547465018 100644
--- a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
+++ b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
@@ -6,6 +6,8 @@
<link rel="stylesheet" type="text/css"
href="${request.static_url(reqt)}"/>
</tal:block>
+ <script src="${request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}"
+ type="text/javascript"></script>
<tal:block tal:repeat="reqt view.reqts['js']">
<script src="${request.static_url(reqt)}"
type="text/javascript"></script>
diff --git a/docs/quick_tutorial/functional_testing.rst b/docs/quick_tutorial/functional_testing.rst
index 09b05b0bc..6f1544e79 100644
--- a/docs/quick_tutorial/functional_testing.rst
+++ b/docs/quick_tutorial/functional_testing.rst
@@ -10,7 +10,7 @@ Background
==========
Unit tests are a common and popular approach to test-driven development
-(TDD.) In web applications, though, the templating and entire apparatus
+(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.
diff --git a/docs/quick_tutorial/ini.rst b/docs/quick_tutorial/ini.rst
index b8720711b..36942c767 100644
--- a/docs/quick_tutorial/ini.rst
+++ b/docs/quick_tutorial/ini.rst
@@ -88,7 +88,7 @@ the Pyramid chapter on
- ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial``
-- The projects's ``setup.py`` has defined an "entry point" (lines 9-10)
+- The projects's ``setup.py`` has defined an "entry point" (lines 9-12)
for the project "main" entry point of ``tutorial:main``
- The ``tutorial`` package's ``__init__`` has a ``main`` function
@@ -131,6 +131,8 @@ Extra Credit
#. The entry point in ``setup.py`` didn't mention ``__init__.py`` when
it declared ``tutorial:main`` function. Why not?
+#. What is the purpose of ``**settings``? What does the ``**`` signify?
+
.. seealso::
:ref:`project_narr`,
:ref:`scaffolding_chapter`,
@@ -138,7 +140,3 @@ Extra Credit
:ref:`environment_chapter`,
:ref:`paste_chapter`
-Extra Credit
-============
-
-#. What is the purpose of ``**settings``? What does the ``**`` signify?
diff --git a/docs/quick_tutorial/jinja2.rst b/docs/quick_tutorial/jinja2.rst
index 613542349..2121803f9 100644
--- a/docs/quick_tutorial/jinja2.rst
+++ b/docs/quick_tutorial/jinja2.rst
@@ -6,7 +6,7 @@
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
+used in Flask and modeled 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.
diff --git a/docs/quick_tutorial/jinja2/tutorial/home.jinja2 b/docs/quick_tutorial/jinja2/tutorial/home.jinja2
index 975323169..20d33b733 100644
--- a/docs/quick_tutorial/jinja2/tutorial/home.jinja2
+++ b/docs/quick_tutorial/jinja2/tutorial/home.jinja2
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: {{ name }}</title>
+ <title>Quick Tutorial: {{ name }}</title>
</head>
<body>
<h1>Hi {{ name }}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/json/tutorial/home.pt b/docs/quick_tutorial/json/tutorial/home.pt
index a0cc08e7a..fd4ef8764 100644
--- a/docs/quick_tutorial/json/tutorial/home.pt
+++ b/docs/quick_tutorial/json/tutorial/home.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/logging.rst b/docs/quick_tutorial/logging.rst
index e07d23d6d..5d29cd196 100644
--- a/docs/quick_tutorial/logging.rst
+++ b/docs/quick_tutorial/logging.rst
@@ -16,9 +16,9 @@ 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``, has a number of lines that
+scaffold generated in your ``development.ini`` has 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.)
+messages sent by Pyramid, for example, when a new request comes in.
Objectives
==========
@@ -42,6 +42,12 @@ Steps
.. literalinclude:: logging/tutorial/views.py
:linenos:
+#. Finally let's edit ``development.ini`` configuration file
+ to enable logging for our Pyramid application:
+
+ .. literalinclude:: logging/development.ini
+ :language: ini
+
#. Make sure the tests still pass:
.. code-block:: bash
@@ -61,15 +67,10 @@ Steps
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::
+In our configuration file ``development.ini``, 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
diff --git a/docs/quick_tutorial/logging/tutorial/home.pt b/docs/quick_tutorial/logging/tutorial/home.pt
index a0cc08e7a..fd4ef8764 100644
--- a/docs/quick_tutorial/logging/tutorial/home.pt
+++ b/docs/quick_tutorial/logging/tutorial/home.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes.rst b/docs/quick_tutorial/more_view_classes.rst
index 9cc4cc520..afbb7cc3a 100644
--- a/docs/quick_tutorial/more_view_classes.rst
+++ b/docs/quick_tutorial/more_view_classes.rst
@@ -47,7 +47,7 @@ Objectives
- Dispatch one route/URL to multiple views based on request data
-- Share stated and logic between views and templates via the view class
+- Share states and logic between views and templates via the view class
Steps
=====
@@ -95,6 +95,23 @@ Steps
.. literalinclude:: more_view_classes/tutorial/delete.pt
:language: html
+#. Our tests in ``more_view_classes/tutorial/tests.py`` fail, so let's modify
+ them:
+
+ .. literalinclude:: more_view_classes/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+ .
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.248s
+
+ OK
+
#. Run your Pyramid application with:
.. code-block:: bash
@@ -125,7 +142,7 @@ Specifically:
- 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
+In this step we show, using the following information as criteria, how to
decide which view to use:
- Method of the HTTP request (``GET``, ``POST``, etc.)
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/delete.pt b/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
index 67cc8bf09..7bd4d3b0d 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${page_title}</title>
+ <title>Quick Tutorial: ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/edit.pt b/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
index 1bd204065..523a4ce5d 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+ <title>Quick Tutorial: ${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
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/hello.pt b/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
index 8a39aed09..40b00bfe4 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+ <title>Quick Tutorial: ${view.view_name} - ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
@@ -13,4 +13,4 @@
<input type="submit" name="form.delete" value="Delete"/>
</form>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/home.pt b/docs/quick_tutorial/more_view_classes/tutorial/home.pt
index fa9016705..fa0436f7e 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/home.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/home.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+ <title>Quick Tutorial: ${view.view_name} - ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
@@ -9,4 +9,4 @@
<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
+</html>
diff --git a/docs/quick_tutorial/package.rst b/docs/quick_tutorial/package.rst
index 8fb052d5b..54a6a0bd9 100644
--- a/docs/quick_tutorial/package.rst
+++ b/docs/quick_tutorial/package.rst
@@ -3,7 +3,7 @@
============================================
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
+Pyramid puts to good use. In this step we redo "Hello World" as a
minimum Python package inside a minimum Python project.
Background
@@ -93,7 +93,7 @@ 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.
+same name in each step of the tutorial, to avoid unnecessary retyping.
Above this ``tutorial`` directory we have the files that handle the
packaging of this project. At the moment, all we need is a
diff --git a/docs/quick_tutorial/request_response.rst b/docs/quick_tutorial/request_response.rst
index 504803804..4f8de0221 100644
--- a/docs/quick_tutorial/request_response.rst
+++ b/docs/quick_tutorial/request_response.rst
@@ -46,14 +46,17 @@ Steps
#. Simplify the routes in ``request_response/tutorial/__init__.py``:
.. literalinclude:: request_response/tutorial/__init__.py
+ :linenos:
#. We only need one view in ``request_response/tutorial/views.py``:
.. literalinclude:: request_response/tutorial/views.py
+ :linenos:
#. Update the tests in ``request_response/tutorial/tests.py``:
.. literalinclude:: request_response/tutorial/tests.py
+ :linenos:
#. Now run the tests:
diff --git a/docs/quick_tutorial/requirements.rst b/docs/quick_tutorial/requirements.rst
index b5778ea42..a737ede0e 100644
--- a/docs/quick_tutorial/requirements.rst
+++ b/docs/quick_tutorial/requirements.rst
@@ -58,11 +58,8 @@ Steps
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.
+Download the latest standard Python 3.3+ release (not development release)
+from `python.org <https://www.python.org/downloads/>`_.
Windows and Mac OS X users can download and run an installer.
@@ -73,8 +70,7 @@ 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
+or may `build Python 3.3 from source
<http://pyramid.readthedocs.org/en/master/narr/install.html#package-manager-
method>`_.
@@ -86,13 +82,21 @@ 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.
+``quick_tutorial``. The following tree diagram shows how this will be
+structured and where our virtual environment will reside as we proceed through
+the tutorial:
+
+.. code-block:: text
+
+ └── ~
+ └── projects
+ └── quick_tutorial
+ ├── env
+ └── step_one
+ ├── intro
+ │ ├── __init__.py
+ │ └── app.py
+ └── setup.py
For Linux, the commands to do so are as follows:
@@ -136,11 +140,11 @@ environment. We set an environment variable to save typing later.
.. code-block:: bash
# Mac and Linux
- $ export VENV=~/projects/quick_tutorial/env33/
+ $ export VENV=~/projects/quick_tutorial/env
# Windows
# TODO: This command does not work
- c:\> set VENV=c:\projects\quick_tutorial\env33
+ c:\> set VENV=c:\projects\quick_tutorial\env
.. _create-a-virtual-environment:
diff --git a/docs/quick_tutorial/routing.rst b/docs/quick_tutorial/routing.rst
index 54dff5c39..1b79a5889 100644
--- a/docs/quick_tutorial/routing.rst
+++ b/docs/quick_tutorial/routing.rst
@@ -14,7 +14,7 @@ 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
+Previously 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
diff --git a/docs/quick_tutorial/routing/tutorial/home.pt b/docs/quick_tutorial/routing/tutorial/home.pt
index f2b991059..b68e96338 100644
--- a/docs/quick_tutorial/routing/tutorial/home.pt
+++ b/docs/quick_tutorial/routing/tutorial/home.pt
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>${name}</h1>
<p>First: ${first}, Last: ${last}</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/sessions.rst b/docs/quick_tutorial/sessions.rst
index b4887beb8..f97405500 100644
--- a/docs/quick_tutorial/sessions.rst
+++ b/docs/quick_tutorial/sessions.rst
@@ -89,7 +89,7 @@ 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.
+as it will be tossed out during the second web request.
Flash messages are a technique where messages can be stored between
requests, using sessions, then removed when they finally get displayed.
diff --git a/docs/quick_tutorial/sessions/tutorial/home.pt b/docs/quick_tutorial/sessions/tutorial/home.pt
index 0b27ba1d8..50342e52f 100644
--- a/docs/quick_tutorial/sessions/tutorial/home.pt
+++ b/docs/quick_tutorial/sessions/tutorial/home.pt
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
<p>Count: ${view.counter}</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/static_assets.rst b/docs/quick_tutorial/static_assets.rst
index 19d33f00f..3a7496ec7 100644
--- a/docs/quick_tutorial/static_assets.rst
+++ b/docs/quick_tutorial/static_assets.rst
@@ -25,7 +25,7 @@ Steps
$ 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
+#. We add a call ``config.add_static_view`` in
``static_assets/tutorial/__init__.py``:
.. literalinclude:: static_assets/tutorial/__init__.py
diff --git a/docs/quick_tutorial/static_assets/tutorial/home.pt b/docs/quick_tutorial/static_assets/tutorial/home.pt
index 5d347f057..57867a1ff 100644
--- a/docs/quick_tutorial/static_assets/tutorial/home.pt
+++ b/docs/quick_tutorial/static_assets/tutorial/home.pt
@@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${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
+</html>
diff --git a/docs/quick_tutorial/templating.rst b/docs/quick_tutorial/templating.rst
index d73067f48..cf56d2a96 100644
--- a/docs/quick_tutorial/templating.rst
+++ b/docs/quick_tutorial/templating.rst
@@ -112,7 +112,7 @@ 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
+to 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.
diff --git a/docs/quick_tutorial/templating/tutorial/home.pt b/docs/quick_tutorial/templating/tutorial/home.pt
index a0cc08e7a..fd4ef8764 100644
--- a/docs/quick_tutorial/templating/tutorial/home.pt
+++ b/docs/quick_tutorial/templating/tutorial/home.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/unit_testing.rst b/docs/quick_tutorial/unit_testing.rst
index f8a33b39d..4cb7ef714 100644
--- a/docs/quick_tutorial/unit_testing.rst
+++ b/docs/quick_tutorial/unit_testing.rst
@@ -24,7 +24,7 @@ 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,
+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.
diff --git a/docs/quick_tutorial/view_classes.rst b/docs/quick_tutorial/view_classes.rst
index 58ab43e40..50a7ee0af 100644
--- a/docs/quick_tutorial/view_classes.rst
+++ b/docs/quick_tutorial/view_classes.rst
@@ -51,7 +51,7 @@ Steps
: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
+ so let's modify them to import the view class and make an instance
before getting a response:
.. literalinclude:: view_classes/tutorial/tests.py
@@ -88,7 +88,7 @@ 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.
+``@view_defaults`` decorator 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
diff --git a/docs/quick_tutorial/views.rst b/docs/quick_tutorial/views.rst
index 529bba0a4..6728925fd 100644
--- a/docs/quick_tutorial/views.rst
+++ b/docs/quick_tutorial/views.rst
@@ -92,7 +92,7 @@ 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
+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,
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index 6c98b6f3a..b0a8c155d 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -1,19 +1,20 @@
+.. _wiki_adding_authorization:
+
====================
-Adding Authorization
+Adding authorization
====================
:app:`Pyramid` provides facilities for :term:`authentication` and
-:term:`authorization`. We'll make use of both features to provide security
-to our application. Our application currently allows anyone with access to
-the server to view, edit, and add pages to our wiki. We'll change that
-to allow only people who are members of a *group* named ``group:editors``
-to add and edit wiki pages but we'll continue allowing
-anyone with access to the server to view pages.
-
-We will also add a login page and a logout link on all the
-pages. The login page will be shown when a user is denied
-access to any of the views that require a permission, instead of
-a default "403 Forbidden" page.
+::term:`authorization`. We'll make use of both features to provide security
+:to our application. Our application currently allows anyone with access to
+:the server to view, edit, and add pages to our wiki. We'll change that to
+:allow only people who are members of a *group* named ``group:editors`` to add
+:and edit wiki pages but we'll continue allowing anyone with access to the
+:server to view pages.
+
+We will also add a login page and a logout link on all the pages. The login
+page will be shown when a user is denied access to any of the views that
+require permission, instead of a default "403 Forbidden" page.
We will implement the access control with the following steps:
@@ -28,12 +29,13 @@ Then we will add the login and logout feature:
* Add ``login`` and ``logout`` views (``views.py``).
* Add a login template (``login.pt``).
-* Make the existing views return a ``logged_in`` flag to the renderer (``views.py``).
+* Make the existing views return a ``logged_in`` flag to the renderer
+ (``views.py``).
* Add a "Logout" link to be shown when logged in and viewing or editing a page
(``view.pt``, ``edit.pt``).
-Access Control
+Access control
--------------
Add users and groups
@@ -49,11 +51,9 @@ following content:
The ``groupfinder`` function accepts a userid and a request and
returns one of these values:
-- If the userid exists in the system, it will return a
- sequence of group identifiers (or an empty sequence if the user
- isn't a member of any groups).
-- If the userid *does not* exist in the system, it will
- return ``None``.
+- If the userid exists in the system, it will return a sequence of group
+ identifiers (or an empty sequence if the user isn't a member of any groups).
+- If the userid *does not* exist in the system, it will return ``None``.
For example, ``groupfinder('editor', request )`` returns ``['group:editor']``,
``groupfinder('viewer', request)`` returns ``[]``, and ``groupfinder('admin',
@@ -61,9 +61,8 @@ request)`` returns ``None``. We will use ``groupfinder()`` as an
:term:`authentication policy` "callback" that will provide the
:term:`principal` or principals for a user.
-In a production system, user and group
-data will most often come from a database, but here we use "dummy"
-data to represent user and groups sources.
+In a production system, user and group data will most often come from a
+database, but here we use "dummy" data to represent user and groups sources.
Add an ACL
~~~~~~~~~~
@@ -81,44 +80,42 @@ Add the following lines to the ``Wiki`` class:
.. literalinclude:: src/authorization/tutorial/models.py
:lines: 9-13
:linenos:
+ :lineno-start: 9
:emphasize-lines: 4-5
:language: python
-We import :data:`~pyramid.security.Allow`, an action that
-means that permission is allowed, and
-:data:`~pyramid.security.Everyone`, a special :term:`principal`
-that is associated to all requests. Both are used in the
+We import :data:`~pyramid.security.Allow`, an action that means that
+permission is allowed, and :data:`~pyramid.security.Everyone`, a special
+:term:`principal` that is associated to all requests. Both are used in the
:term:`ACE` entries that make up the ACL.
-The ACL is a list that needs to be named `__acl__` and be an
-attribute of a class. We define an :term:`ACL` with two
-:term:`ACE` entries: the first entry allows any user the `view`
-permission, and the second entry allows the ``group:editors``
-principal the `edit` permission.
+The ACL is a list that needs to be named `__acl__` and be an attribute of a
+class. We define an :term:`ACL` with two :term:`ACE` entries: the first entry
+allows any user the `view` permission. The second entry allows the
+``group:editors`` principal the `edit` permission.
-The ``Wiki`` class that contains the ACL is the :term:`resource`
-constructor for the :term:`root` resource, which is
-a ``Wiki`` instance. The ACL is
-provided to each view in the :term:`context` of the request, as
-the ``context`` attribute.
+The ``Wiki`` class that contains the ACL is the :term:`resource` constructor
+for the :term:`root` resource, which is a ``Wiki`` instance. The ACL is
+provided to each view in the :term:`context` of the request as the ``context``
+attribute.
It's only happenstance that we're assigning this ACL at class scope. An ACL
can be attached to an object *instance* too; this is how "row level security"
can be achieved in :app:`Pyramid` applications. We actually need only *one*
ACL for the entire system, however, because our security requirements are
-simple, so this feature is not demonstrated. See
-:ref:`assigning_acls` for more information about what an
-:term:`ACL` represents.
+simple, so this feature is not demonstrated. See :ref:`assigning_acls` for
+more information about what an :term:`ACL` represents.
-Add Authentication and Authorization Policies
+Add authentication and authorization policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open ``tutorial/__init__.py`` and
-add these import statements:
+Open ``tutorial/tutorial/__init__.py`` and add the highlighted import
+statements:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 4-5,8
+ :lines: 1-8
:linenos:
+ :emphasize-lines: 4-5,8
:language: python
Now add those policies to the configuration:
@@ -126,15 +123,16 @@ Now add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 18-23
:linenos:
+ :lineno-start: 18
:emphasize-lines: 1-3,5-6
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added.
-We are enabling an ``AuthTktAuthenticationPolicy``, it is based in an
-auth ticket that may be included in the request, and an
-``ACLAuthorizationPolicy`` that uses an ACL to determine the allow or deny
-outcome for a view.
+We are enabling an ``AuthTktAuthenticationPolicy``, which is based in an auth
+ticket that may be included in the request. We are also enabling an
+``ACLAuthorizationPolicy``, which uses an ACL to determine the *allow* or
+*deny* outcome for a view.
Note that the :class:`pyramid.authentication.AuthTktAuthenticationPolicy`
constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is
@@ -144,235 +142,231 @@ machinery represented by this policy: it is required. The ``callback`` is the
Add permission declarations
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open ``tutorial/tutorial/views.py``. Add a ``permission='edit'`` parameter
-to the ``@view_config`` decorator for ``add_page()`` and
-``edit_page()``, for example:
+Open ``tutorial/tutorial/views.py`` and add a ``permission='edit'`` parameter
+to the ``@view_config`` decorators for ``add_page()`` and ``edit_page()``:
-.. code-block:: python
- :linenos:
- :emphasize-lines: 3
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 50-52
+ :emphasize-lines: 2-3
+ :language: python
- @view_config(name='add_page', context='.models.Wiki',
- renderer='templates/edit.pt',
- permission='edit')
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 70-72
+ :emphasize-lines: 2-3
+ :language: python
-(Only the highlighted line, along with its preceding comma,
-needs to be added.)
+Only the highlighted lines, along with their preceding commas, need to be
+edited and added.
-The result is that only users who possess the ``edit``
-permission at the time of the request may invoke those two views.
+The result is that only users who possess the ``edit`` permission at the time
+of the request may invoke those two views.
-Add a ``permission='view'`` parameter to the ``@view_config``
-decorator for ``view_wiki()`` and ``view_page()``, like this:
+Add a ``permission='view'`` parameter to the ``@view_config`` decorator for
+``view_wiki()`` and ``view_page()`` as follows:
-.. code-block:: python
- :linenos:
- :emphasize-lines: 2
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 23-24
+ :emphasize-lines: 1-2
+ :language: python
- @view_config(context='.models.Page', renderer='templates/view.pt',
- permission='view')
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 28-29
+ :emphasize-lines: 1-2
+ :language: python
-(Only the highlighted line, along with its preceding comma,
-needs to be added.)
+Only the highlighted lines, along with their preceding commas, need to be
+edited and added.
This allows anyone to invoke these two views.
-We are done with the changes needed to control access. The
-changes that follow will add the login and logout feature.
+We are done with the changes needed to control access. The changes that
+follow will add the login and logout feature.
-Login, Logout
+Login, logout
-------------
-Add Login and Logout Views
+Add login and logout views
~~~~~~~~~~~~~~~~~~~~~~~~~~
-We'll add a ``login`` view which renders a login form and processes
-the post from the login form, checking credentials.
+We'll add a ``login`` view which renders a login form and processes the post
+from the login form, checking credentials.
-We'll also add a ``logout`` view callable to our application and
-provide a link to it. This view will clear the credentials of the
-logged in user and redirect back to the front page.
+We'll also add a ``logout`` view callable to our application and provide a
+link to it. This view will clear the credentials of the logged in user and
+redirect back to the front page.
-Add the following import statements to the
-head of ``tutorial/tutorial/views.py``:
+Add the following import statements to the head of
+``tutorial/tutorial/views.py``:
.. literalinclude:: src/authorization/tutorial/views.py
:lines: 6-17
- :linenos:
- :emphasize-lines: 3,6-11
+ :emphasize-lines: 1-12
:language: python
-(Only the highlighted lines, with other necessary modifications,
-need to be added.)
+All the highlighted lines need to be added or edited.
-:meth:`~pyramid.view.forbidden_view_config` will be used
-to customize the default 403 Forbidden page.
-:meth:`~pyramid.security.remember` and
-:meth:`~pyramid.security.forget` help to create and
-expire an auth ticket cookie.
+:meth:`~pyramid.view.forbidden_view_config` will be used to customize the
+default 403 Forbidden page. :meth:`~pyramid.security.remember` and
+:meth:`~pyramid.security.forget` help to create and expire an auth ticket
+cookie.
-Now add the ``login`` and ``logout`` views:
+Now add the ``login`` and ``logout`` views at the end of the file:
.. literalinclude:: src/authorization/tutorial/views.py
- :lines: 82-120
+ :lines: 82-116
:linenos:
+ :lineno-start: 82
:language: python
``login()`` has two decorators:
-- a ``@view_config`` decorator which associates it with the
- ``login`` route and makes it visible when we visit ``/login``,
-- a ``@forbidden_view_config`` decorator which turns it into
- a :term:`forbidden view`. ``login()`` will be invoked
- when a user tries to execute a view callable for which they lack
- authorization. For example, if a user has not logged in
- and tries to add or edit a Wiki page, they will be shown the
- login form before being allowed to continue.
+- a ``@view_config`` decorator which associates it with the ``login`` route
+ and makes it visible when we visit ``/login``,
+- a ``@forbidden_view_config`` decorator which turns it into a
+ :term:`forbidden view`. ``login()`` will be invoked when a user tries to
+ execute a view callable for which they lack authorization. For example, if
+ a user has not logged in and tries to add or edit a Wiki page, they will be
+ shown the login form before being allowed to continue.
-The order of these two :term:`view configuration` decorators
-is unimportant.
+The order of these two :term:`view configuration` decorators is unimportant.
-``logout()`` is decorated with a ``@view_config`` decorator
-which associates it with the ``logout`` route. It will be
-invoked when we visit ``/logout``.
+``logout()`` is decorated with a ``@view_config`` decorator which associates
+it with the ``logout`` route. It will be invoked when we visit ``/logout``.
Add the ``login.pt`` Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Create ``tutorial/tutorial/templates/login.pt`` with the following
-content:
+Create ``tutorial/tutorial/templates/login.pt`` with the following content:
.. literalinclude:: src/authorization/tutorial/templates/login.pt
- :language: xml
+ :language: html
-The above template is referred in the login view that we just added
-in ``views.py``.
+The above template is referenced in the login view that we just added in
+``views.py``.
-Return a logged_in flag to the renderer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Return a ``logged_in`` flag to the renderer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Add a ``logged_in`` parameter to the return value of
-``view_page()``, ``edit_page()`` and ``add_page()``,
-like this:
+Open ``tutorial/tutorial/views.py`` again. Add a ``logged_in`` parameter to
+the return value of ``view_page()``, ``edit_page()``, and ``add_page()`` as
+follows:
-.. code-block:: python
- :linenos:
- :emphasize-lines: 4
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 47-48
+ :emphasize-lines: 1-2
+ :language: python
- return dict(page = page,
- content = content,
- edit_url = edit_url,
- logged_in = request.authenticated_userid)
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 67-68
+ :emphasize-lines: 1-2
+ :language: python
-(Only the highlighted line and a trailing comma on the preceding
-line need to be added.)
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 75-77
+ :emphasize-lines: 2-3
+ :language: python
+
+Only the highlighted lines need to be added or edited.
The :meth:`pyramid.request.Request.authenticated_userid` will be ``None`` if
-the user is not authenticated, or a user id if the user is authenticated.
+the user is not authenticated, or a userid if the user is authenticated.
Add a "Logout" link when logged in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open ``tutorial/tutorial/templates/edit.pt`` and
-``tutorial/tutorial/templates/view.pt`` and add this within the
-``<div id="right" class="app-welcome align-right">`` div:
+``tutorial/tutorial/templates/view.pt`` and add the following code as
+indicated by the highlighted lines.
-.. code-block:: xml
-
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
+.. literalinclude:: src/authorization/tutorial/templates/edit.pt
+ :lines: 34-38
+ :emphasize-lines: 3-5
+ :language: html
-The attribute ``tal:condition="logged_in"`` will make the element be
-included when ``logged_in`` is any user id. The link will invoke
-the logout view. The above element will not be included if ``logged_in``
-is ``None``, such as when a user is not authenticated.
+The attribute ``tal:condition="logged_in"`` will make the element be included
+when ``logged_in`` is any user id. The link will invoke the logout view. The
+above element will not be included if ``logged_in`` is ``None``, such as when
+a user is not authenticated.
-Seeing Our Changes
-------------------
+Reviewing our changes
+---------------------
-Our ``tutorial/tutorial/__init__.py`` will look something like this
-when we're done:
+Our ``tutorial/tutorial/__init__.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
:emphasize-lines: 4-5,8,18-20,22-23
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/models.py`` will look something like this
-when we're done:
+Our ``tutorial/tutorial/models.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/models.py
:linenos:
:emphasize-lines: 4-7,12-13
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/views.py`` will look something like this
-when we're done:
+Our ``tutorial/tutorial/views.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/views.py
:linenos:
:emphasize-lines: 8,11-15,17,24,29,48,52,68,72,80,82-120
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/templates/edit.pt`` template will look
-something like this when we're done:
+Our ``tutorial/tutorial/templates/edit.pt`` template will look like this when
+we're done:
.. literalinclude:: src/authorization/tutorial/templates/edit.pt
:linenos:
- :emphasize-lines: 41-43
- :language: xml
+ :emphasize-lines: 36-38
+ :language: html
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/templates/view.pt`` template will look
-something like this when we're done:
+Our ``tutorial/tutorial/templates/view.pt`` template will look like this when
+we're done:
.. literalinclude:: src/authorization/tutorial/templates/view.pt
:linenos:
- :emphasize-lines: 41-43
- :language: xml
+ :emphasize-lines: 36-38
+ :language: html
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Viewing the Application in a Browser
+Viewing the application in a browser
------------------------------------
We can finally examine our application in a browser (See
-:ref:`wiki-start-the-application`). Launch a browser and visit
-each of the following URLs, check that the result is as expected:
-
-- ``http://localhost:6543/`` invokes the
- ``view_wiki`` view. This always redirects to the ``view_page`` view
- of the ``FrontPage`` Page resource. It is executable by any user.
-
-- ``http://localhost:6543/FrontPage`` invokes
- the ``view_page`` view of the ``FrontPage`` Page resource. This is because
- it's the :term:`default view` (a view without a ``name``) for ``Page``
- resources. It is executable by any user.
-
-- ``http://localhost:6543/FrontPage/edit_page``
- invokes the edit view for the FrontPage object. It is executable by
- only the ``editor`` user. If a different user (or the anonymous
- user) invokes it, a login form will be displayed. Supplying the
- credentials with the username ``editor``, password ``editor`` will
- display the edit page form.
-
-- ``http://localhost:6543/add_page/SomePageName``
- invokes the add view for a page. It is executable by only
- the ``editor`` user. If a different user (or the anonymous user)
- invokes it, a login form will be displayed. Supplying the
- credentials with the username ``editor``, password ``editor`` will
- display the edit page form.
-
-- After logging in (as a result of hitting an edit or add page
- and submitting the login form with the ``editor``
- credentials), we'll see a Logout link in the upper right hand
- corner. When we click it, we're logged out, and redirected
- back to the front page.
+:ref:`wiki-start-the-application`). Launch a browser and visit each of the
+following URLs, checking that the result is as expected:
+
+- http://localhost:6543/ invokes the ``view_wiki`` view. This always
+ redirects to the ``view_page`` view of the ``FrontPage`` Page resource. It
+ is executable by any user.
+
+- http://localhost:6543/FrontPage invokes the ``view_page`` view of the
+ ``FrontPage`` Page resource. This is because it's the :term:`default view`
+ (a view without a ``name``) for ``Page`` resources. It is executable by any
+ user.
+
+- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
+ FrontPage object. It is executable by only the ``editor`` user. If a
+ different user (or the anonymous user) invokes it, a login form will be
+ displayed. Supplying the credentials with the username ``editor``, password
+ ``editor`` will display the edit page form.
+
+- http://localhost:6543/add_page/SomePageName invokes the add view for a page.
+ It is executable by only the ``editor`` user. If a different user (or the
+ anonymous user) invokes it, a login form will be displayed. Supplying the
+ credentials with the username ``editor``, password ``editor`` will display
+ the edit page form.
+
+- After logging in (as a result of hitting an edit or add page and submitting
+ the login form with the ``editor`` credentials), we'll see a Logout link in
+ the upper right hand corner. When we click it, we're logged out, and
+ redirected back to the front page.
diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst
index cdf52b73e..0484ebf17 100644
--- a/docs/tutorials/wiki/basiclayout.rst
+++ b/docs/tutorials/wiki/basiclayout.rst
@@ -2,25 +2,27 @@
Basic Layout
============
-The starter files generated by the ``zodb`` scaffold are basic, but
+The starter files generated by the ``zodb`` scaffold are very basic, but
they provide a good orientation for the high-level patterns common to most
-:term:`traversal` -based :app:`Pyramid` (and :term:`ZODB` -based) projects.
+:term:`traversal`-based (and :term:`ZODB`-based) :app:`Pyramid` projects.
-Application Configuration with ``__init__.py``
-------------------------------------------------
+Application configuration with ``__init__.py``
+----------------------------------------------
A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file. Even if empty, this marks a directory as a Python
-package. Our application uses ``__init__.py`` both as a package marker and
-to contain application configuration code.
+package. We use ``__init__.py`` both as a marker, indicating the directory
+in which it's contained is a package, and to contain application configuration
+code.
When you run the application using the ``pserve`` command using the
-``development.ini`` generated config file, the application configuration
-points at a Setuptools *entry point* described as ``egg:tutorial``. In our
-application, because the application's ``setup.py`` file says so, this entry
-point happens to be the ``main`` function within the file named
-``__init__.py``:
+``development.ini`` generated configuration file, the application
+configuration points at a Setuptools *entry point* described as
+``egg:tutorial``. In our application, because the application's ``setup.py``
+file says so, this entry point happens to be the ``main`` function within the
+file named ``__init__.py``. Let's take a look at the code and describe what
+it does:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:linenos:
@@ -28,17 +30,19 @@ point happens to be the ``main`` function within the file named
#. *Lines 1-3*. Perform some dependency imports.
-#. *Lines 6-8*. Define a root factory for our Pyramid application.
+#. *Lines 6-8*. Define a :term:`root factory` for our Pyramid application.
-#. *Line 14*. We construct a :term:`Configurator` with a :term:`root
- factory` and the settings keywords parsed by :term:`PasteDeploy`. The root
+#. *Line 11*. ``__init__.py`` defines a function named ``main``.
+
+#. *Line 14*. We construct a :term:`Configurator` with a root
+ factory and the settings keywords parsed by :term:`PasteDeploy`. The root
factory is named ``root_factory``.
#. *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
+#. *Line 16*. Register a "static view", which answers requests whose URL
+ paths 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
and image files, for us, in this case, at
@@ -63,7 +67,7 @@ point happens to be the ``main`` function within the file named
:meth:`pyramid.config.Configurator.make_wsgi_app` method
to return a :term:`WSGI` application.
-Resources and Models with ``models.py``
+Resources and models with ``models.py``
---------------------------------------
:app:`Pyramid` uses the word :term:`resource` to describe objects arranged
@@ -93,13 +97,12 @@ Here is the source for ``models.py``:
root* object. It is called on *every request* to the
:app:`Pyramid` application. It also performs bootstrapping by
*creating* an application root (inside the ZODB root object) if one
- does not already exist. It is used by the "root_factory" we've defined
+ does not already exist. It is used by the ``root_factory`` we've defined
in our ``__init__.py``.
- We do so by first seeing if the database has the persistent
- application root. If not, we make an instance, store it, and
- commit the transaction. We then return the application root
- object.
+ Bootstrapping is done by first seeing if the database has the persistent
+ application root. If not, we make an instance, store it, and commit the
+ transaction. We then return the application root object.
Views With ``views.py``
-----------------------
@@ -171,6 +174,6 @@ opposed to the tutorial :term:`package` directory) looks like this:
Note the existence of a ``[app:main]`` section which specifies our WSGI
application. Our ZODB database settings are specified as the
``zodbconn.uri`` setting within this section. This value, and the other
-values within this section are passed as ``**settings`` to the ``main``
+values within this section, are passed as ``**settings`` to the ``main``
function we defined in ``__init__.py`` when the server is started via
``pserve``.
diff --git a/docs/tutorials/wiki/definingmodels.rst b/docs/tutorials/wiki/definingmodels.rst
index 49372179f..859e902ab 100644
--- a/docs/tutorials/wiki/definingmodels.rst
+++ b/docs/tutorials/wiki/definingmodels.rst
@@ -15,7 +15,7 @@ single instance of the "Wiki" class will serve as a container for "Page"
objects, which will be instances of the "Page" class.
-Delete the Database
+Delete the database
-------------------
In the next step, we're going to remove the ``MyModel`` Python model
@@ -32,12 +32,19 @@ Edit ``models.py``
.. note::
- There is nothing automagically special about the filename ``models.py``. A
+ There is nothing special about the filename ``models.py``. A
project may have many models throughout its codebase in arbitrarily named
- files. Files implementing models often have ``model`` in their filenames,
+ files. Files implementing models often have ``model`` in their filenames
or they may live in a Python subpackage of your application package named
``models``, but this is only by convention.
+Open ``tutorial/tutorial/models.py`` file and edit it to look like the
+following:
+
+.. literalinclude:: src/models/tutorial/models.py
+ :linenos:
+ :language: python
+
The first thing we want to do is remove the ``MyModel`` class from the
generated ``models.py`` file. The ``MyModel`` class is only a sample and
we're not going to use it.
@@ -70,17 +77,7 @@ front page) into the Wiki within the ``appmaker``. This will provide
:term:`traversal` a :term:`resource tree` to work against when it attempts to
resolve URLs to resources.
-Look at the Result of Our Edits to ``models.py``
-------------------------------------------------
-
-The result of all of our edits to ``models.py`` will end up looking
-something like this:
-
-.. literalinclude:: src/models/tutorial/models.py
- :linenos:
- :language: python
-
-View the Application in a Browser
+View the application in a browser
---------------------------------
We can't. At this point, our system is in a "non-runnable" state; we'll need
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index e06468267..ed173a706 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -17,7 +17,7 @@ assumed to return a :term:`response` object.
interchangeably as necessary. In :term:`traversal` based applications,
URLs are mapped to a context :term:`resource`, and since our
:term:`resource tree` also represents our application's
- "domain model", we're often interested in the context, because
+ "domain model", we're often interested in the context because
it represents the persistent storage of our application. For
this reason, in this tutorial we define views as callables that
accept ``context`` in the callable argument list. If you do
@@ -35,35 +35,80 @@ Declaring Dependencies in Our ``setup.py`` File
The view code in our application will depend on a package which is not a
dependency of the original "tutorial" application. The original "tutorial"
application was generated by the ``pcreate`` command; it doesn't know
-about our custom application requirements. We need to add a dependency on
-the ``docutils`` package to our ``tutorial`` package's ``setup.py`` file by
-assigning this dependency to the ``install_requires`` parameter in the
-``setup`` function.
+about our custom application requirements.
-Our resulting ``setup.py`` should look like so:
+We need to add a dependency on the ``docutils`` package to our ``tutorial``
+package's ``setup.py`` file by assigning this dependency to the ``requires``
+parameter in the ``setup()`` function.
+
+Open ``tutorial/setup.py`` and edit it to look like the following:
.. literalinclude:: src/views/setup.py
:linenos:
+ :emphasize-lines: 20
+ :language: python
+
+Only the highlighted line needs to be added.
+
+Running ``setup.py develop``
+============================
+
+Since a new software dependency was added, you will need to run ``python
+setup.py develop`` again inside the root of the ``tutorial`` package to obtain
+and register the newly added dependency distribution.
+
+Make sure your current working directory is the root of the project (the
+directory in which ``setup.py`` lives) and execute the following command.
+
+On UNIX:
+
+.. code-block:: text
+
+ $ cd tutorial
+ $ $VENV/bin/python setup.py develop
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut> cd tutorial
+ c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py develop
+
+Success executing this command will end with a line to the console something
+like::
+
+ Finished processing dependencies for tutorial==0.0
+
+Adding view functions in ``views.py``
+=====================================
+
+It's time for a major change. Open ``tutorial/tutorial/views.py`` and edit it
+to look like the following:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :linenos:
:language: python
-.. note:: After these new dependencies are added, you will need to
- rerun ``python setup.py develop`` inside the root of the
- ``tutorial`` package to obtain and register the newly added
- dependency package.
+We added some imports and created a regular expression to find "WikiWords".
+
+We got rid of the ``my_view`` view function and its decorator that was added
+when we originally rendered the ``zodb`` scaffold. It was only an example and
+isn't relevant to our application.
+
+Then we added four :term:`view callable` functions to our ``views.py``
+module:
-Adding View Functions
-=====================
+* ``view_wiki()`` - Displays the wiki itself. It will answer on the root URL.
+* ``view_page()`` - Displays an individual page.
+* ``add_page()`` - Allows the user to add a page.
+* ``edit_page()`` - Allows the user to edit a page.
-We're going to add four :term:`view callable` functions to our ``views.py``
-module. One view named ``view_wiki`` will display the wiki itself (it will
-answer on the root URL), another named ``view_page`` will display an
-individual page, another named ``add_page`` will allow a page to be added,
-and a final view named ``edit_page`` will allow a page to be edited.
+We'll describe each one briefly in the following sections.
.. note::
There is nothing special about the filename ``views.py``. A project may
- have many view callables throughout its codebase in arbitrarily-named
+ have many view callables throughout its codebase in arbitrarily named
files. Files implementing view callables often have ``view`` in their
filenames (or may live in a Python subpackage of your application package
named ``views``), but this is only by convention.
@@ -71,44 +116,55 @@ and a final view named ``edit_page`` will allow a page to be edited.
The ``view_wiki`` view function
-------------------------------
-Here is the code for the ``view_wiki`` view function and its decorator, which
-will be added to ``views.py``:
+Following is the code for the ``view_wiki`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views.py
:lines: 12-14
+ :lineno-start: 12
+ :linenos:
:language: python
-The ``view_wiki`` function will be configured to respond as the default view
-callable for a Wiki resource. We'll provide it with a ``@view_config``
-decorator which names the class ``tutorial.models.Wiki`` as its context.
-This means that when a Wiki resource is the context, and no :term:`view name`
-exists in the request, this view will be used. The view configuration
-associated with ``view_wiki`` does not use a ``renderer`` because the view
-callable always returns a :term:`response` object rather than a dictionary.
-No renderer is necessary when a view returns a response object.
-
-The ``view_wiki`` view callable always redirects to the URL of a Page
-resource named "FrontPage". To do so, it returns an instance of the
+.. note:: In our code, we use an *import* that is *relative* to our package
+ named ``tutorial``, meaning we can omit the name of the package in the
+ ``import`` and ``context`` statements. In our narrative, however, we refer
+ to a *class* and thus we use the *absolute* form, meaning that the name of
+ the package is included.
+
+``view_wiki()`` is the :term:`default view` that gets called when a request is
+made to the root URL of our wiki. It always redirects to an URL which
+represents the path to our "FrontPage".
+
+We provide it with a ``@view_config`` decorator which names the class
+``tutorial.models.Wiki`` as its context. This means that when a Wiki resource
+is the context and no :term:`view name` exists in the request, then this view
+will be used. The view configuration associated with ``view_wiki`` does not
+use a ``renderer`` because the view callable always returns a :term:`response`
+object rather than a dictionary. No renderer is necessary when a view returns
+a response object.
+
+The ``view_wiki`` view callable always redirects to the URL of a Page resource
+named "FrontPage". To do so, it returns an instance of the
:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
-the :class:`pyramid.interfaces.IResponse` interface like
-:class:`pyramid.response.Response` does).
-:meth:`pyramid.request.Request.resource_url` constructs a URL to the
+the :class:`pyramid.interfaces.IResponse` interface, like
+:class:`pyramid.response.Response` does). It uses the
+:meth:`pyramid.request.Request.route_url` API to construct an URL to the
``FrontPage`` page resource (i.e., ``http://localhost:6543/FrontPage``), and
-uses it as the "location" of the HTTPFound response, forming an HTTP
+uses it as the "location" of the ``HTTPFound`` response, forming an HTTP
redirect.
The ``view_page`` view function
-------------------------------
-Here is the code for the ``view_page`` view function and its decorator, which
-will be added to ``views.py``:
+Here is the code for the ``view_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views.py
:lines: 16-33
+ :lineno-start: 16
+ :linenos:
:language: python
-The ``view_page`` function will be configured to respond as the default view
-of a Page resource. We'll provide it with a ``@view_config`` decorator which
+The ``view_page`` function is configured to respond as the default view
+of a Page resource. We provide it with a ``@view_config`` decorator which
names the class ``tutorial.models.Page`` as its context. This means that
when a Page resource is the context, and no :term:`view name` exists in the
request, this view will be used. We inform :app:`Pyramid` this view will use
@@ -116,9 +172,9 @@ the ``templates/view.pt`` template file as a ``renderer``.
The ``view_page`` function generates the :term:`reStructuredText` body of a
page (stored as the ``data`` attribute of the context passed to the view; the
-context will be a Page resource) as HTML. Then it substitutes an HTML anchor
-for each *WikiWord* reference in the rendered HTML using a compiled regular
-expression.
+context will be a ``Page`` resource) as HTML. Then it substitutes an HTML
+anchor for each *WikiWord* reference in the rendered HTML using a compiled
+regular expression.
The curried function named ``check`` is used as the first argument to
``wikiwords.sub``, indicating that it should be called to provide a value for
@@ -133,8 +189,8 @@ As a result, the ``content`` variable is now a fully formed bit of HTML
containing various view and add links for WikiWords based on the content of
our current page resource.
-We then generate an edit URL (because it's easier to do here than in the
-template), and we wrap up a number of arguments in a dictionary and return
+We then generate an edit URL because it's easier to do here than in the
+template, and we wrap up a number of arguments in a dictionary and return
it.
The arguments we wrap into a dictionary include ``page``, ``content``, and
@@ -153,22 +209,23 @@ callable. In the ``view_wiki`` view callable, we unconditionally return a
The ``add_page`` view function
------------------------------
-Here is the code for the ``add_page`` view function and its decorator, which
-will be added to ``views.py``:
+Here is the code for the ``add_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views.py
:lines: 35-50
+ :lineno-start: 35
+ :linenos:
:language: python
-The ``add_page`` function will be configured to respond when the context
-resource is a Wiki and the :term:`view name` is ``add_page``. We'll provide
-it with a ``@view_config`` decorator which names the string ``add_page`` as
-its :term:`view name` (via name=), the class ``tutorial.models.Wiki`` as its
-context, and the renderer named ``templates/edit.pt``. This means that when
-a Wiki resource is the context, and a :term:`view name` named ``add_page``
+The ``add_page`` function is configured to respond when the context resource
+is a Wiki and the :term:`view name` is ``add_page``. We provide it with a
+``@view_config`` decorator which names the string ``add_page`` as its
+:term:`view name` (via ``name=``), the class ``tutorial.models.Wiki`` as its
+context, and the renderer named ``templates/edit.pt``. This means that when a
+Wiki resource is the context, and a :term:`view name` named ``add_page``
exists as the result of traversal, this view will be used. We inform
-:app:`Pyramid` this view will use the ``templates/edit.pt`` template file as
-a ``renderer``. We share the same template between add and edit views, thus
+:app:`Pyramid` this view will use the ``templates/edit.pt`` template file as a
+``renderer``. We share the same template between add and edit views, thus
``edit.pt`` instead of ``add.pt``.
The ``add_page`` function will be invoked when a user clicks on a WikiWord
@@ -181,7 +238,7 @@ Page resource).
The request :term:`subpath` in :app:`Pyramid` is the sequence of names that
are found *after* the :term:`view name` in the URL segments given in the
``PATH_INFO`` of the WSGI request as the result of :term:`traversal`. If our
-add view is invoked via, e.g. ``http://localhost:6543/add_page/SomeName``,
+add view is invoked via, e.g., ``http://localhost:6543/add_page/SomeName``,
the :term:`subpath` will be a tuple: ``('SomeName',)``.
The add view takes the zeroth element of the subpath (the wiki page name),
@@ -198,7 +255,7 @@ order to satisfy the edit form's desire to have *some* page object exposed as
``page``, and we'll render the template to a response.
If the view rendering *is* a result of a form submission (if the expression
-``'form.submitted' in request.params`` is ``True``), we scrape the page body
+``'form.submitted' in request.params`` is ``True``), we grab the page body
from the form data, create a Page object using the name in the subpath and
the page body, and save it into "our context" (the Wiki) using the
``__setitem__`` method of the context. We then redirect back to the
@@ -207,15 +264,16 @@ the page body, and save it into "our context" (the Wiki) using the
The ``edit_page`` view function
-------------------------------
-Here is the code for the ``edit_page`` view function and its decorator, which
-will be added to ``views.py``:
+Here is the code for the ``edit_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views.py
:lines: 52-60
+ :lineno-start: 52
+ :linenos:
:language: python
-The ``edit_page`` function will be configured to respond when the context is
-a Page resource and the :term:`view name` is ``edit_page``. We'll provide it
+The ``edit_page`` function is configured to respond when the context is
+a Page resource and the :term:`view name` is ``edit_page``. We provide it
with a ``@view_config`` decorator which names the string ``edit_page`` as its
:term:`view name` (via ``name=``), the class ``tutorial.models.Page`` as its
context, and the renderer named ``templates/edit.pt``. This means that when
@@ -240,26 +298,16 @@ If the view execution *is* a result of a form submission (if the expression
attribute of the page context. It then redirects to the default view of the
context (the page), which will always be the ``view_page`` view.
-Viewing the Result of all Our Edits to ``views.py``
-===================================================
-
-The result of all of our edits to ``views.py`` will leave it looking like
-this:
-
-.. literalinclude:: src/views/tutorial/views.py
- :linenos:
- :language: python
-
-Adding Templates
+Adding templates
================
The ``view_page``, ``add_page`` and ``edit_page`` views that we've added
-reference a :term:`template`. Each template is a :term:`Chameleon` :term:`ZPT`
-template. These templates will live in the ``templates`` directory of our
-tutorial package. Chameleon templates must have a ``.pt`` extension to be
-recognized as such.
+reference a :term:`template`. Each template is a :term:`Chameleon`
+:term:`ZPT` template. These templates will live in the ``templates``
+directory of our tutorial package. Chameleon templates must have a ``.pt``
+extension to be recognized as such.
-The ``view.pt`` Template
+The ``view.pt`` template
------------------------
Create ``tutorial/tutorial/templates/view.pt`` and add the following
@@ -267,20 +315,18 @@ content:
.. literalinclude:: src/views/tutorial/templates/view.pt
:linenos:
- :language: xml
+ :language: html
This template is used by ``view_page()`` for displaying a single
wiki page. It includes:
-- A ``div`` element that is replaced with the ``content``
- value provided by the view (rows 45-47). ``content``
- contains HTML, so the ``structure`` keyword is used
- to prevent escaping it (i.e. changing ">" to "&gt;", etc.)
-- A link that points
- at the "edit" URL which invokes the ``edit_page`` view for
- the page being viewed (rows 49-51).
+- A ``div`` element that is replaced with the ``content`` value provided by
+ the view (lines 36-38). ``content`` contains HTML, so the ``structure``
+ keyword is used to prevent escaping it (i.e., changing ">" to "&gt;", etc.)
+- A link that points at the "edit" URL which invokes the ``edit_page`` view
+ for the page being viewed (lines 40-42).
-The ``edit.pt`` Template
+The ``edit.pt`` template
------------------------
Create ``tutorial/tutorial/templates/edit.pt`` and add the following
@@ -288,66 +334,58 @@ content:
.. literalinclude:: src/views/tutorial/templates/edit.pt
:linenos:
- :language: xml
+ :language: html
-This template is used by ``add_page()`` and ``edit_page()`` for adding
-and editing a wiki page. It displays
-a page containing a form that includes:
+This template is used by ``add_page()`` and ``edit_page()`` for adding and
+editing a wiki page. It displays a page containing a form that includes:
- A 10 row by 60 column ``textarea`` field named ``body`` that is filled
- with any existing page data when it is rendered (rows 46-47).
-- A submit button that has the name ``form.submitted`` (row 48).
-
-The form POSTs back to the "save_url" argument supplied
-by the view (row 45). The view will use the ``body`` and
-``form.submitted`` values.
-
-.. note:: Our templates use a ``request`` object that
- none of our tutorial views return in their dictionary.
- ``request`` is one of several
- names that are available "by default" in a template when a template
- renderer is used. See :ref:`renderer_system_values` for
- information about other names that are available by default
- when a template is used as a renderer.
-
-Static Assets
+ with any existing page data when it is rendered (line 45).
+- A submit button that has the name ``form.submitted`` (line 48).
+
+The form POSTs back to the ``save_url`` argument supplied by the view (line
+43). The view will use the ``body`` and ``form.submitted`` values.
+
+.. note:: Our templates use a ``request`` object that none of our tutorial
+ views return in their dictionary. ``request`` is one of several names that
+ are available "by default" in a template when a template renderer is used.
+ See :ref:`renderer_system_values` for information about other names that
+ are available by default when a template is used as a renderer.
+
+Static assets
-------------
-Our templates name a single static asset named ``pylons.css``. We don't need
-to create this file within our package's ``static`` directory because it was
-provided at the time we created the project. This file is a little too long to
-replicate within the body of this guide, however it is available `online
-<https://github.com/Pylons/pyramid/blob/master/docs/tutorials/wiki/src/views/tutorial/static/pylons.css>`_.
+Our templates name static assets, including CSS and images. We don't need
+to create these files within our package's ``static`` directory because they
+were provided at the time we created the project.
-This CSS file will be accessed via
-e.g. ``/static/pylons.css`` by virtue of the call to
+As an example, the CSS file will be accessed via
+``http://localhost:6543/static/theme.css`` by virtue of the call to the
``add_static_view`` directive we've made in the ``__init__.py`` file. Any
number and type of static assets can be placed in this directory (or
-subdirectories) and are just referred to by URL.
+subdirectories) and are just referred to by URL or by using the convenience
+method ``static_url``, e.g.,
+``request.static_url('<package>:static/foo.css')`` within templates.
-Viewing the Application in a Browser
+Viewing the application in a browser
====================================
We can finally examine our application in a browser (See
:ref:`wiki-start-the-application`). Launch a browser and visit
-each of the following URLs, check that the result is as expected:
+each of the following URLs, checking that the result is as expected:
-- ``http://localhost:6543/`` invokes the ``view_wiki``
- view. This always redirects to the ``view_page`` view of the ``FrontPage``
- Page resource.
+- http://localhost:6543/ invokes the ``view_wiki`` view. This always
+ redirects to the ``view_page`` view of the ``FrontPage`` Page resource.
-- ``http://localhost:6543/FrontPage/`` invokes
- the ``view_page`` view of the front page resource. This is
- because it's the :term:`default view` (a view without a ``name``) for Page
- resources.
+- http://localhost:6543/FrontPage/ invokes the ``view_page`` view of the front
+ page resource. This is because it's the :term:`default view` (a view
+ without a ``name``) for Page resources.
-- ``http://localhost:6543/FrontPage/edit_page``
- invokes the edit view for the ``FrontPage`` Page resource.
+- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
+ ``FrontPage`` Page resource.
-- ``http://localhost:6543/add_page/SomePageName``
- invokes the add view for a Page.
+- http://localhost:6543/add_page/SomePageName invokes the add view for a Page.
-- To generate an error, visit ``http://localhost:6543/add_page`` which
- will generate an ``IndexErrorr: tuple index out of range`` error.
- You'll see an interactive traceback
- facility provided by :term:`pyramid_debugtoolbar`.
+- To generate an error, visit http://localhost:6543/add_page which will
+ generate an ``IndexErrorr: tuple index out of range`` error. You'll see an
+ interactive traceback facility provided by :term:`pyramid_debugtoolbar`.
diff --git a/docs/tutorials/wiki/design.rst b/docs/tutorials/wiki/design.rst
index 28380bd66..49c30d29a 100644
--- a/docs/tutorials/wiki/design.rst
+++ b/docs/tutorials/wiki/design.rst
@@ -2,22 +2,22 @@
Design
==========
-Following is a quick overview of our wiki application, to help
-us understand the changes that we will be doing next in our
-default files generated by the ``zodb`` scaffold.
+Following is a quick overview of the design of our wiki application, to help
+us understand the changes that we will be making as we work through the
+tutorial.
Overall
-------
-We choose to use ``reStructuredText`` markup in the wiki text.
-Translation from reStructuredText to HTML is provided by the
-widely used ``docutils`` Python module. We will add this module
-in the dependency list on the project ``setup.py`` file.
+We choose to use :term:`reStructuredText` markup in the wiki text. Translation
+from reStructuredText to HTML is provided by the widely used ``docutils``
+Python module. We will add this module in the dependency list on the project
+``setup.py`` file.
Models
------
-The root resource, named *Wiki*, will be a mapping of wiki page
+The root resource named ``Wiki`` will be a mapping of wiki page
names to page resources. The page resources will be instances
of a *Page* class and they store the text content.
@@ -29,9 +29,9 @@ To add a page to the wiki, a new instance of the page resource
is created and its name and reference are added to the Wiki
mapping.
-A page named *FrontPage* containing the text *This is the front
-page*, will be created when the storage is initialized, and will
-be used as the wiki home page.
+A page named ``FrontPage`` containing the text *This is the front page*, will
+be created when the storage is initialized, and will be used as the wiki home
+page.
Views
-----
@@ -57,14 +57,13 @@ use to do this are below.
corresponding passwords.
- GROUPS, a dictionary mapping :term:`userids <userid>` to a
- list of groups to which they belong to.
+ list of groups to which they belong.
-- ``groupfinder``, an *authorization callback* that looks up
- USERS and GROUPS. It will be provided in a new
- *security.py* file.
+- ``groupfinder``, an *authorization callback* that looks up USERS and
+ GROUPS. It will be provided in a new ``security.py`` file.
-- An :term:`ACL` is attached to the root :term:`resource`. Each
- row below details an :term:`ACE`:
+- An :term:`ACL` is attached to the root :term:`resource`. Each row below
+ details an :term:`ACE`:
+----------+----------------+----------------+
| Action | Principal | Permission |
@@ -125,7 +124,7 @@ listed in the following table:
| | | | authenticate. | | |
| | | | | | |
| | | | - If authentication | | |
-| | | | successful, | | |
+| | | | succeeds, | | |
| | | | redirect to the | | |
| | | | page that we | | |
| | | | came from. | | |
@@ -145,6 +144,6 @@ listed in the following table:
when there is no view name.
.. [2] Pyramid will return a default 404 Not Found page
if the page *PageName* does not exist yet.
-.. [3] pyramid.exceptions.Forbidden is reached when a
+.. [3] ``pyramid.exceptions.Forbidden`` is reached when a
user tries to invoke a view that is
not authorized by the authorization policy.
diff --git a/docs/tutorials/wiki/distributing.rst b/docs/tutorials/wiki/distributing.rst
index 9c63cf0bd..fee50a1cf 100644
--- a/docs/tutorials/wiki/distributing.rst
+++ b/docs/tutorials/wiki/distributing.rst
@@ -2,11 +2,11 @@
Distributing Your Application
=============================
-Once your application works properly, you can create a "tarball" from
-it by using the ``setup.py sdist`` command. The following commands
-assume your current working directory is the ``tutorial`` package
-we've created and that the parent directory of the ``tutorial``
-package is a virtualenv representing a :app:`Pyramid` environment.
+Once your application works properly, you can create a "tarball" from it by
+using the ``setup.py sdist`` command. The following commands assume your
+current working directory is the ``tutorial`` package we've created and that
+the parent directory of the ``tutorial`` package is a virtualenv representing
+a :app:`Pyramid` environment.
On UNIX:
@@ -27,16 +27,14 @@ The output of such a command will be something like:
running sdist
# .. more output ..
creating dist
- tar -cf dist/tutorial-0.1.tar tutorial-0.1
- gzip -f9 dist/tutorial-0.1.tar
- removing 'tutorial-0.1' (and everything under it)
-
-Note that this command creates a tarball in the "dist" subdirectory
-named ``tutorial-0.1.tar.gz``. You can send this file to your friends
-to show them your cool new application. They should be able to
-install it by pointing the ``easy_install`` command directly at it.
-Or you can upload it to `PyPI <http://pypi.python.org>`_ and share it
-with the rest of the world, where it can be downloaded via
-``easy_install`` remotely like any other package people download from
-PyPI.
-
+ tar -cf dist/tutorial-0.0.tar tutorial-0.0
+ gzip -f9 dist/tutorial-0.0.tar
+ removing 'tutorial-0.0' (and everything under it)
+
+Note that this command creates a tarball in the "dist" subdirectory named
+``tutorial-0.0.tar.gz``. You can send this file to your friends to show them
+your cool new application. They should be able to install it by pointing the
+``easy_install`` command directly at it. Or you can upload it to `PyPI
+<http://pypi.python.org>`_ and share it with the rest of the world, where it
+can be downloaded via ``easy_install`` remotely like any other package people
+download from PyPI.
diff --git a/docs/tutorials/wiki/index.rst b/docs/tutorials/wiki/index.rst
index 981d135c7..89c026dac 100644
--- a/docs/tutorials/wiki/index.rst
+++ b/docs/tutorials/wiki/index.rst
@@ -3,10 +3,10 @@
ZODB + Traversal Wiki Tutorial
==============================
-This tutorial introduces a :term:`traversal` -based :app:`Pyramid`
-application to a developer familiar with Python. It will be most familiar to
-developers with previous :term:`Zope` experience. When we're done with the
-tutorial, the developer will have created a basic Wiki application with
+This tutorial introduces a :term:`ZODB` and :term:`traversal`-based
+:app:`Pyramid` application to a developer familiar with Python. It will be
+most familiar to developers with previous :term:`Zope` experience. When the
+is finished, the developer will have created a basic Wiki application with
authentication.
For cut and paste purposes, the source code for all stages of this
diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst
index b51254b92..20df389c6 100644
--- a/docs/tutorials/wiki/installation.rst
+++ b/docs/tutorials/wiki/installation.rst
@@ -2,125 +2,218 @@
Installation
============
-Preparation
-===========
+Before you begin
+================
-Follow the steps in :ref:`installing_chapter`, but name the virtualenv
-directory ``pyramidtut``.
+This tutorial assumes that you have already followed the steps in
+:ref:`installing_chapter`, except **do not create a virtualenv or install
+Pyramid**. Thereby you will satisfy the following requirements.
-Preparation, UNIX
------------------
+* Python interpreter is installed on your operating system
+* :term:`setuptools` or :term:`distribute` is installed
+* :term:`virtualenv` is installed
+Create directory to contain the project
+---------------------------------------
-#. Switch to the ``pyramidtut`` directory:
+We need a workspace for our project files.
- .. code-block:: text
+On UNIX
+^^^^^^^
- $ cd pyramidtut
+.. code-block:: text
+
+ $ mkdir ~/pyramidtut
+
+On Windows
+^^^^^^^^^^
+
+.. code-block:: text
+
+ c:\> mkdir pyramidtut
-#. Install tutorial dependencies:
+Create and use a virtual Python environment
+-------------------------------------------
- .. code-block:: text
+Next let's create a `virtualenv` workspace for our project. We will
+use the `VENV` environment variable instead of the absolute path of the
+virtual environment.
- $ $VENV/bin/easy_install docutils pyramid_tm pyramid_zodbconn \
- pyramid_debugtoolbar nose coverage
+On UNIX
+^^^^^^^
-Preparation, Windows
---------------------
+.. code-block:: text
+
+ $ export VENV=~/pyramidtut
+ $ virtualenv $VENV
+ New python executable in /home/foo/env/bin/python
+ Installing setuptools.............done.
+
+On Windows
+^^^^^^^^^^
+.. code-block:: text
+
+ c:\> set VENV=c:\pyramidtut
-#. Switch to the ``pyramidtut`` directory:
+Versions of Python use different paths, so you will need to adjust the
+path to the command for your Python version.
- .. code-block:: text
+Python 2.7:
- c:\> cd pyramidtut
+.. code-block:: text
-#. Install tutorial dependencies:
+ c:\> c:\Python27\Scripts\virtualenv %VENV%
- .. code-block:: text
+Python 3.2:
+
+.. code-block:: text
+
+ c:\> c:\Python32\Scripts\virtualenv %VENV%
+
+Install Pyramid and tutorial dependencies into the virtual Python environment
+-----------------------------------------------------------------------------
+
+On UNIX
+^^^^^^^
+
+.. code-block:: text
+
+ $ $VENV/bin/easy_install docutils pyramid_tm pyramid_zodbconn \
+ pyramid_debugtoolbar nose coverage
+
+On Windows
+^^^^^^^^^^
+
+.. code-block:: text
- c:\pyramidtut> %VENV%\Scripts\easy_install docutils pyramid_tm \
- pyramid_zodbconn pyramid_debugtoolbar nose coverage
+ c:\> %VENV%\Scripts\easy_install docutils pyramid_tm pyramid_zodbconn \
+ pyramid_debugtoolbar nose coverage
+
+Change Directory to Your Virtual Python Environment
+---------------------------------------------------
+
+Change directory to the ``pyramidtut`` directory.
+
+On UNIX
+^^^^^^^
+
+.. code-block:: text
+
+ $ cd pyramidtut
+
+On Windows
+^^^^^^^^^^
+
+.. code-block:: text
+
+ c:\> cd pyramidtut
.. _making_a_project:
-Make a Project
-==============
+Making a project
+================
+
+Your next step is to create a project. For this tutorial, we will use
+the :term:`scaffold` named ``zodb``, which generates an application
+that uses :term:`ZODB` and :term:`traversal`.
-Your next step is to create a project. For this tutorial, we will use the
-:term:`scaffold` named ``zodb``, which generates an application
-that uses :term:`ZODB` and :term:`traversal`. :app:`Pyramid`
-supplies a variety of scaffolds to generate sample projects.
+:app:`Pyramid` supplies a variety of scaffolds to generate sample
+projects. We will use `pcreate`—a script that comes with Pyramid to
+quickly and easily generate scaffolds, usually with a single command—to
+create the scaffold for our project.
-The below instructions assume your current working directory is the
-"virtualenv" named "pyramidtut".
+By passing `zodb` into the `pcreate` command, the script creates
+the files needed to use ZODB. By passing in our application name
+`tutorial`, the script inserts that application name into all the
+required files.
-On UNIX:
+The below instructions assume your current working directory is "pyramidtut".
+
+On UNIX
+-------
.. code-block:: text
- $ $VENV/bin/pcreate -s zodb tutorial
+ $ $VENV/bin/pcreate -s zodb tutorial
-On Windows:
+On Windows
+----------
.. code-block:: text
c:\pyramidtut> %VENV%\Scripts\pcreate -s zodb tutorial
-.. note:: You don't have to call it `tutorial` -- the code uses
- relative paths for imports and finding templates and static
- resources.
+.. note:: If you are using Windows, the ``zodb``
+ scaffold may not deal gracefully with installation into a
+ location that contains spaces in the path. If you experience
+ startup problems, try putting both the virtualenv and the project
+ into directories that do not contain spaces in their paths.
-.. note:: If you are using Windows, the ``zodb`` scaffold
- doesn't currently deal gracefully with installation into a location
- that contains spaces in the path. If you experience startup
- problems, try putting both the virtualenv and the project into
- directories that do not contain spaces in their paths.
+.. _installing_project_in_dev_mode_zodb:
-Install the Project in "Development Mode"
-=========================================
+Installing the project in development mode
+==========================================
In order to do development on the project easily, you must "register"
the project as a development egg in your workspace using the
-``setup.py develop`` command. In order to do so, cd to the "tutorial"
+``setup.py develop`` command. In order to do so, cd to the `tutorial`
directory you created in :ref:`making_a_project`, and run the
-"setup.py develop" command using virtualenv Python interpreter.
+``setup.py develop`` command using the virtualenv Python interpreter.
-On UNIX:
+On UNIX
+-------
.. code-block:: text
- $ cd tutorial
- $ $VENV/bin/python setup.py develop
+ $ cd tutorial
+ $ $VENV/bin/python setup.py develop
-On Windows:
+On Windows
+----------
.. code-block:: text
- C:\pyramidtut> cd tutorial
- C:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py develop
+ c:\pyramidtut> cd tutorial
+ c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py develop
+
+The console will show `setup.py` checking for packages and installing
+missing packages. Success executing this command will show a line like
+the following::
+
+ Finished processing dependencies for tutorial==0.0
.. _running_tests:
-Run the Tests
+Run the tests
=============
After you've installed the project in development mode, you may run
the tests for the project.
-On UNIX:
+On UNIX
+-------
.. code-block:: text
- $ $VENV/bin/python setup.py test -q
+ $ $VENV/bin/python setup.py test -q
-On Windows:
+On Windows
+----------
.. code-block:: text
- c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py test -q
+ c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py test -q
-Expose Test Coverage Information
+For a successful test run, you should see output that ends like this::
+
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.094s
+
+ OK
+
+Expose test coverage information
================================
You can run the ``nosetests`` command to see test coverage
@@ -129,48 +222,73 @@ test`` does but provides additional "coverage" information, exposing
which lines of your project are "covered" (or not covered) by the
tests.
-On UNIX:
+On UNIX
+-------
.. code-block:: text
- $ $VENV/bin/nosetests --cover-package=tutorial --cover-erase --with-coverage
+ $ $VENV/bin/nosetests --cover-package=tutorial --cover-erase --with-coverage
-On Windows:
+On Windows
+----------
.. code-block:: text
- c:\pyramidtut\tutorial> %VENV%\Scripts\nosetests --cover-package=tutorial ^
- --cover-erase --with-coverage
+ c:\pyramidtut\tutorial> %VENV%\Scripts\nosetests --cover-package=tutorial \
+ --cover-erase --with-coverage
+
+If successful, you will see output something like this::
-Looks like the code in the ``zodb`` scaffold for ZODB projects is
-missing some test coverage, particularly in the file named
-``models.py``.
+ .
+ Name Stmts Miss Cover Missing
+ --------------------------------------------------
+ tutorial.py 12 7 42% 7-8, 14-18
+ tutorial/models.py 10 6 40% 9-14
+ tutorial/views.py 4 0 100%
+ --------------------------------------------------
+ TOTAL 26 13 50%
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.392s
+
+ OK
+
+Looks like our package doesn't quite have 100% test coverage.
.. _wiki-start-the-application:
-Start the Application
+Start the application
=====================
Start the application.
-On UNIX:
+On UNIX
+-------
.. code-block:: text
- $ $VENV/bin/pserve development.ini --reload
+ $ $VENV/bin/pserve development.ini --reload
-On Windows:
+On Windows
+----------
.. code-block:: text
- c:\pyramidtut\tutorial> %VENV%\Scripts\pserve development.ini --reload
+ c:\pyramidtut\tutorial> %VENV%\Scripts\pserve development.ini --reload
.. note::
Your OS firewall, if any, may pop up a dialog asking for authorization
to allow python to accept incoming network connections.
-Visit the Application in a Browser
+If successful, you will see something like this on your console::
+
+ Starting subprocess with file monitor
+ Starting server in PID 95736.
+ serving on http://0.0.0.0:6543
+
+This means the server is ready to accept requests.
+
+Visit the application in a browser
==================================
In a browser, visit `http://localhost:6543/ <http://localhost:6543>`_. You
@@ -181,7 +299,7 @@ page. You can read more about the purpose of the icon at
:ref:`debug_toolbar`. It allows you to get information about your
application while you develop.
-Decisions the ``zodb`` Scaffold Has Made For You
+Decisions the ``zodb`` scaffold has made for you
================================================
Creating a project using the ``zodb`` scaffold makes the following
@@ -189,11 +307,11 @@ assumptions:
- you are willing to use :term:`ZODB` as persistent storage
-- you are willing to use :term:`traversal` to map URLs to code.
+- you are willing to use :term:`traversal` to map URLs to code
.. note::
:app:`Pyramid` supports any persistent storage mechanism (e.g., a SQL
- database or filesystem files). :app:`Pyramid` also supports an additional
+ database or filesystem files). It also supports an additional
mechanism to map URLs to code (:term:`URL dispatch`). However, for the
purposes of this tutorial, we'll only be using traversal and ZODB.
diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py
index 5ab4f73cd..e2e96379d 100644
--- a/docs/tutorials/wiki/src/authorization/setup.py
+++ b/docs/tutorials/wiki/src/authorization/setup.py
@@ -11,10 +11,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'pyramid_zodbconn',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'ZODB3',
'waitress',
'docutils',
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/favicon.ico b/docs/tutorials/wiki/src/authorization/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css b/docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki/src/authorization/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki/src/authorization/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/theme.css b/docs/tutorials/wiki/src/authorization/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/theme.min.css b/docs/tutorials/wiki/src/authorization/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
index c3a0acf6b..823fa8972 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
@@ -1,58 +1,72 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.__name__} - Pyramid tutorial wiki (based on
- TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.__name__} - Pyramid tutorial wiki (based on
+ TurboGears 20-Minute Wiki)</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p tal:condition="logged_in" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ <p>
+ Editing <strong><span tal:replace="page.__name__">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <form action="${save_url}" method="post">
+ <div class="form-group">
+ <textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Editing <b><span tal:replace="page.__name__">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- <div id="bottom">
- <div class="bottom">
- <form action="${save_url}" method="post">
- <textarea name="body" tal:content="page.data" rows="10"
- cols="60"/><br/>
- <input type="submit" name="form.submitted" value="Save"/>
- </form>
- </div>
- </div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
index 3612dccde..4a938e9bb 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
@@ -1,54 +1,74 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>Login - Pyramid tutorial wiki (based on TurboGears
- 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Login - Pyramid tutorial wiki (based on
+ TurboGears 20-Minute Wiki)</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p>
+ <strong>
+ Login
+ </strong><br>
+ <span tal:replace="message"></span>
+ </p>
+ <form action="${url}" method="post">
+ <input type="hidden" name="came_from" value="${came_from}">
+ <div class="form-group">
+ <label for="login">Username</label>
+ <input type="text" name="login" value="${login}">
+ </div>
+ <div class="form-group">
+ <label for="password">Password</label>
+ <input type="password" name="password" value="${password}">
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Log In" class="btn btn-default">Log In</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- <b>Login</b><br/>
- <span tal:replace="message"/>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <form action="${url}" method="post">
- <input type="hidden" name="came_from" value="${came_from}"/>
- <input type="text" name="login" value="${login}"/><br/>
- <input type="password" name="password"
- value="${password}"/><br/>
- <input type="submit" name="form.submitted" value="Log In"/>
- </form>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
index 13b41f823..1b30f42b6 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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="/static/favicon.ico" />
- <link rel="stylesheet" href="/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="/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="/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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>ZODB Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">ZODB scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
index 90e20764d..fa35d758d 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
@@ -1,61 +1,72 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.__name__} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.__name__} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p tal:condition="logged_in" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ <p>
+ Viewing <strong><span tal:replace="page.__name__">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Viewing <b><span tal:replace="page.__name__">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
- </div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div tal:replace="structure content">
- Page text goes here.
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <p>
- <a tal:attributes="href edit_url" href="">
- Edit this page
- </a>
- </p>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/basiclayout/setup.py b/docs/tutorials/wiki/src/basiclayout/setup.py
index da79881ab..58a454f80 100644
--- a/docs/tutorials/wiki/src/basiclayout/setup.py
+++ b/docs/tutorials/wiki/src/basiclayout/setup.py
@@ -11,10 +11,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'pyramid_zodbconn',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'ZODB3',
'waitress',
]
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/favicon.ico b/docs/tutorials/wiki/src/basiclayout/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.min.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
index 13b41f823..1b30f42b6 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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="/static/favicon.ico" />
- <link rel="stylesheet" href="/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="/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="/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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>ZODB Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">ZODB scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/models/setup.py b/docs/tutorials/wiki/src/models/setup.py
index da79881ab..58a454f80 100644
--- a/docs/tutorials/wiki/src/models/setup.py
+++ b/docs/tutorials/wiki/src/models/setup.py
@@ -11,10 +11,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'pyramid_zodbconn',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'ZODB3',
'waitress',
]
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/favicon.ico b/docs/tutorials/wiki/src/models/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/models/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/models/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/ie6.css b/docs/tutorials/wiki/src/models/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki/src/models/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/models/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki/src/models/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki/src/models/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/theme.css b/docs/tutorials/wiki/src/models/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/theme.min.css b/docs/tutorials/wiki/src/models/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki/src/models/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/models/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki/src/models/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
index 13b41f823..1b30f42b6 100644
--- a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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="/static/favicon.ico" />
- <link rel="stylesheet" href="/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="/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="/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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>ZODB Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">ZODB scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py
index 2e7ed2398..b67b702cf 100644
--- a/docs/tutorials/wiki/src/tests/setup.py
+++ b/docs/tutorials/wiki/src/tests/setup.py
@@ -11,10 +11,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'pyramid_zodbconn',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'ZODB3',
'waitress',
'docutils',
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/favicon.ico b/docs/tutorials/wiki/src/tests/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/tests/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/tests/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/ie6.css b/docs/tutorials/wiki/src/tests/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki/src/tests/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/tests/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css b/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki/src/tests/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki/src/tests/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/tests/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/tests/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki/src/tests/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/theme.css b/docs/tutorials/wiki/src/tests/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/theme.min.css b/docs/tutorials/wiki/src/tests/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/tests/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki/src/tests/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
index c3a0acf6b..823fa8972 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
@@ -1,58 +1,72 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.__name__} - Pyramid tutorial wiki (based on
- TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.__name__} - Pyramid tutorial wiki (based on
+ TurboGears 20-Minute Wiki)</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p tal:condition="logged_in" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ <p>
+ Editing <strong><span tal:replace="page.__name__">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <form action="${save_url}" method="post">
+ <div class="form-group">
+ <textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Editing <b><span tal:replace="page.__name__">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- <div id="bottom">
- <div class="bottom">
- <form action="${save_url}" method="post">
- <textarea name="body" tal:content="page.data" rows="10"
- cols="60"/><br/>
- <input type="submit" name="form.submitted" value="Save"/>
- </form>
- </div>
- </div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/login.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/login.pt
index 3612dccde..4a938e9bb 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/login.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/login.pt
@@ -1,54 +1,74 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>Login - Pyramid tutorial wiki (based on TurboGears
- 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Login - Pyramid tutorial wiki (based on
+ TurboGears 20-Minute Wiki)</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p>
+ <strong>
+ Login
+ </strong><br>
+ <span tal:replace="message"></span>
+ </p>
+ <form action="${url}" method="post">
+ <input type="hidden" name="came_from" value="${came_from}">
+ <div class="form-group">
+ <label for="login">Username</label>
+ <input type="text" name="login" value="${login}">
+ </div>
+ <div class="form-group">
+ <label for="password">Password</label>
+ <input type="password" name="password" value="${password}">
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Log In" class="btn btn-default">Log In</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- <b>Login</b><br/>
- <span tal:replace="message"/>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <form action="${url}" method="post">
- <input type="hidden" name="came_from" value="${came_from}"/>
- <input type="text" name="login" value="${login}"/><br/>
- <input type="password" name="password"
- value="${password}"/><br/>
- <input type="submit" name="form.submitted" value="Log In"/>
- </form>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
index 13b41f823..1b30f42b6 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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="/static/favicon.ico" />
- <link rel="stylesheet" href="/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="/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="/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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>ZODB Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">ZODB scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
index 90e20764d..fa35d758d 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
@@ -1,61 +1,72 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.__name__} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.__name__} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p tal:condition="logged_in" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ <p>
+ Viewing <strong><span tal:replace="page.__name__">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Viewing <b><span tal:replace="page.__name__">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
- </div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div tal:replace="structure content">
- Page text goes here.
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <p>
- <a tal:attributes="href edit_url" href="">
- Edit this page
- </a>
- </p>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/views/setup.py b/docs/tutorials/wiki/src/views/setup.py
index 5ab4f73cd..e2e96379d 100644
--- a/docs/tutorials/wiki/src/views/setup.py
+++ b/docs/tutorials/wiki/src/views/setup.py
@@ -11,10 +11,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'pyramid_zodbconn',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'ZODB3',
'waitress',
'docutils',
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/favicon.ico b/docs/tutorials/wiki/src/views/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/footerbg.png b/docs/tutorials/wiki/src/views/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/headerbg.png b/docs/tutorials/wiki/src/views/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/ie6.css b/docs/tutorials/wiki/src/views/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki/src/views/tutorial/static/middlebg.png b/docs/tutorials/wiki/src/views/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki/src/views/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki/src/views/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png b/docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png b/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/theme.css b/docs/tutorials/wiki/src/views/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/theme.min.css b/docs/tutorials/wiki/src/views/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki/src/views/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/transparent.gif b/docs/tutorials/wiki/src/views/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki/src/views/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt b/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
index 24ed2e592..b23f45d56 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
@@ -1,58 +1,69 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.__name__} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.__name__} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p>
+ Editing <strong><span tal:replace="page.__name__">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <form action="${save_url}" method="post">
+ <div class="form-group">
+ <textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Editing <b><span tal:replace="page.__name__">Page Name Goes
- Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <form action="${save_url}" method="post">
- <textarea name="body" tal:content="page.data" rows="10"
- cols="60"/><br/>
- <input type="submit" name="form.submitted" value="Save"/>
- </form>
</div>
</div>
- </div>
- <div id="footer">
- <div class="footer"
- >&copy; Copyright 2008-2011, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
index 50102aa20..1b30f42b6 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
@@ -1,76 +1,66 @@
-<!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="/static/favicon.ico" />
- <link rel="stylesheet" href="/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="/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="/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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>ZODB Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">ZODB scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
- <div id="footer">
- <div class="footer">&copy; Copyright 2008-2012, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/view.pt b/docs/tutorials/wiki/src/views/tutorial/templates/view.pt
index 424c4302a..e7b0dc23e 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/view.pt
@@ -1,61 +1,69 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.__name__} - Pyramid tutorial wiki (based on
- TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="/static/favicon.ico" />
- <link rel="stylesheet"
- href="/static/pylons.css"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="/static/ie6.css"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="/static/pyramid-small.png" />
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
+ TurboGears 20-Minute Wiki)</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ <p>
+ Viewing <strong><span tal:replace="page.__name__">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Viewing <b><span tal:replace="page.__name__">Page Name Goes
- Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div tal:replace="structure content">
- Page text goes here.
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <p>
- <a tal:attributes="href edit_url" href="">
- Edit this page
- </a>
- </p>
</div>
</div>
- </div>
- <div id="footer">
- <div class="footer"
- >&copy; Copyright 2008-2011, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki/tests.rst b/docs/tutorials/wiki/tests.rst
index e724f3e18..e255812fc 100644
--- a/docs/tutorials/wiki/tests.rst
+++ b/docs/tutorials/wiki/tests.rst
@@ -2,38 +2,36 @@
Adding Tests
============
-We will now add tests for the models and the views and a few functional
-tests in the ``tests.py``. Tests ensure that an application works, and
-that it continues to work after some changes are made in the future.
+We will now add tests for the models and the views and a few functional tests
+in ``tests.py``. Tests ensure that an application works, and that it
+continues to work when changes are made in the future.
-
-Test the Models
+Test the models
===============
-We write tests for the model classes and the appmaker. Changing
-``tests.py``, we'll write a separate test class for each model class, and
+We write tests for the ``model`` classes and the ``appmaker``. Changing
+``tests.py``, we'll write a separate test class for each ``model`` class, and
we'll write a test class for the ``appmaker``.
To do so, we'll retain the ``tutorial.tests.ViewTests`` class that was
-generated as part of the ``zodb`` scaffold. We'll add three test
-classes: one for the ``Page`` model named ``PageModelTests``, one for the
-``Wiki`` model named ``WikiModelTests``, and one for the appmaker named
-``AppmakerTests``.
+generated as part of the ``zodb`` scaffold. We'll add three test classes: one
+for the ``Page`` model named ``PageModelTests``, one for the ``Wiki`` model
+named ``WikiModelTests``, and one for the appmaker named ``AppmakerTests``.
-Test the Views
+Test the views
==============
We'll modify our ``tests.py`` file, adding tests for each view function we
-added above. As a result, we'll *delete* the ``ViewTests`` test in the file,
-and add four other test classes: ``ViewWikiTests``, ``ViewPageTests``,
-``AddPageTests``, and ``EditPageTests``. These test the ``view_wiki``,
-``view_page``, ``add_page``, and ``edit_page`` views respectively.
-
+added previously. As a result, we'll *delete* the ``ViewTests`` class that
+the ``zodb`` scaffold provided, and add four other test classes:
+``ViewWikiTests``, ``ViewPageTests``, ``AddPageTests``, and ``EditPageTests``.
+These test the ``view_wiki``, ``view_page``, ``add_page``, and ``edit_page``
+views.
Functional tests
================
-We test the whole application, covering security aspects that are not
+We'll test the whole application, covering security aspects that are not
tested in the unit tests, like logging in, logging out, checking that
the ``viewer`` user cannot add or edit pages, but the ``editor`` user
can, and so on.
@@ -41,14 +39,14 @@ can, and so on.
View the results of all our edits to ``tests.py``
=================================================
-Once we're done with the ``tests.py`` module, it will look a lot like the
-below:
+Open the ``tutorial/tests.py`` module, and edit it such that it appears as
+follows:
.. literalinclude:: src/tests/tutorial/tests.py
:linenos:
:language: python
-Running the Tests
+Running the tests
=================
We can run these tests by using ``setup.py test`` in the same way we did in
@@ -62,7 +60,7 @@ Change the ``requires`` list in ``setup.py`` to include ``WebTest``.
:lines: 11-22
:emphasize-lines: 11
-After we've added a dependency on WebTest in ``setup.py``, we need to rerun
+After we've added a dependency on WebTest in ``setup.py``, we need to run
``setup.py develop`` to get WebTest installed into our virtualenv. Assuming
our shell's current working directory is the "tutorial" distribution
directory:
@@ -94,7 +92,7 @@ On Windows:
c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py test -q
-The expected result looks something like:
+The expected result should look like the following:
.. code-block:: text
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index 90a89d63e..1d810b05b 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -1,27 +1,25 @@
.. _wiki2_adding_authorization:
====================
-Adding Authorization
+Adding authorization
====================
:app:`Pyramid` provides facilities for :term:`authentication` and
:term:`authorization`. We'll make use of both features to provide security
to our application. Our application currently allows anyone with access to
-the server to view, edit, and add pages to our wiki. We'll change that
-to allow only people who are members of a *group* named ``group:editors``
-to add and edit wiki pages but we'll continue allowing
-anyone with access to the server to view pages.
+the server to view, edit, and add pages to our wiki. We'll change that to
+allow only people who are members of a *group* named ``group:editors`` to add
+and edit wiki pages but we'll continue allowing anyone with access to the
+server to view pages.
-We will also add a login page and a logout link on all the
-pages. The login page will be shown when a user is denied
-access to any of the views that require permission, instead of
-a default "403 Forbidden" page.
+We will also add a login page and a logout link on all the pages. The login
+page will be shown when a user is denied access to any of the views that
+require permission, instead of a default "403 Forbidden" page.
We will implement the access control with the following steps:
* Add users and groups (``security.py``, a new module).
-* Add an :term:`ACL` (``models.py`` and
- ``__init__.py``).
+* Add an :term:`ACL` (``models.py`` and ``__init__.py``).
* Add an :term:`authentication policy` and an :term:`authorization policy`
(``__init__.py``).
* Add :term:`permission` declarations to the ``edit_page`` and ``add_page``
@@ -32,12 +30,13 @@ Then we will add the login and logout feature:
* Add routes for /login and /logout (``__init__.py``).
* Add ``login`` and ``logout`` views (``views.py``).
* Add a login template (``login.pt``).
-* Make the existing views return a ``logged_in`` flag to the renderer (``views.py``).
+* Make the existing views return a ``logged_in`` flag to the renderer
+ (``views.py``).
* Add a "Logout" link to be shown when logged in and viewing or editing a page
(``view.pt``, ``edit.pt``).
-Access Control
+Access control
--------------
Add users and groups
@@ -53,21 +52,18 @@ following content:
The ``groupfinder`` function accepts a userid and a request and
returns one of these values:
-- If the userid exists in the system, it will return a
- sequence of group identifiers (or an empty sequence if the user
- isn't a member of any groups).
-- If the userid *does not* exist in the system, it will
- return ``None``.
+- If the userid exists in the system, it will return a sequence of group
+ identifiers (or an empty sequence if the user isn't a member of any groups).
+- If the userid *does not* exist in the system, it will return ``None``.
For example, ``groupfinder('editor', request )`` returns ``['group:editor']``,
-``groupfinder('viewer', request)`` returns [], and ``groupfinder('admin', request)``
-returns ``None``. We will use ``groupfinder()`` as an :term:`authentication policy`
-"callback" that will provide the :term:`principal` or principals
-for a user.
+``groupfinder('viewer', request)`` returns ``[]``, and ``groupfinder('admin',
+request)`` returns ``None``. We will use ``groupfinder()`` as an
+:term:`authentication policy` "callback" that will provide the
+:term:`principal` or principals for a user.
-In a production system, user and group
-data will most often come from a database, but here we use "dummy"
-data to represent user and groups sources.
+In a production system, user and group data will most often come from a
+database, but here we use "dummy" data to represent user and groups sources.
Add an ACL
~~~~~~~~~~
@@ -80,62 +76,60 @@ statement at the head:
:linenos:
:language: python
-Add the following class definition:
+Add the following class definition at the end:
.. literalinclude:: src/authorization/tutorial/models.py
:lines: 33-37
:linenos:
+ :lineno-start: 33
:language: python
-We import :data:`~pyramid.security.Allow`, an action that
-means that permission is allowed:, and
-:data:`~pyramid.security.Everyone`, a special :term:`principal`
-that is associated to all requests. Both are used in the
+We import :data:`~pyramid.security.Allow`, an action that means that
+permission is allowed, and :data:`~pyramid.security.Everyone`, a special
+:term:`principal` that is associated to all requests. Both are used in the
:term:`ACE` entries that make up the ACL.
-The ACL is a list that needs to be named `__acl__` and be an
-attribute of a class. We define an :term:`ACL` with two
-:term:`ACE` entries: the first entry allows any user the `view`
-permission. The second entry allows the ``group:editors``
-principal the `edit` permission.
+The ACL is a list that needs to be named `__acl__` and be an attribute of a
+class. We define an :term:`ACL` with two :term:`ACE` entries: the first entry
+allows any user the `view` permission. The second entry allows the
+``group:editors`` principal the `edit` permission.
-The ``RootFactory`` class that contains the ACL is a :term:`root factory`.
-We need to associate it to our :app:`Pyramid` application, so the ACL is
-provided to each view in the :term:`context` of the request, as
-the ``context`` attribute.
+The ``RootFactory`` class that contains the ACL is a :term:`root factory`. We
+need to associate it to our :app:`Pyramid` application, so the ACL is provided
+to each view in the :term:`context` of the request as the ``context``
+attribute.
-Open ``tutorial/tutorial/__init__.py`` and add a ``root_factory``
-parameter to our :term:`Configurator` constructor, that points to
-the class we created above:
+Open ``tutorial/tutorial/__init__.py`` and add a ``root_factory`` parameter to
+our :term:`Configurator` constructor, that points to the class we created
+above:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 24-25
:linenos:
:emphasize-lines: 2
+ :lineno-start: 16
:language: python
-(Only the highlighted line needs to be added.)
+Only the highlighted line needs to be added.
-We are now providing the ACL to the application. See
-:ref:`assigning_acls` for more information about what an
-:term:`ACL` represents.
+We are now providing the ACL to the application. See :ref:`assigning_acls`
+for more information about what an :term:`ACL` represents.
-.. note::
+.. note:: Although we don't use the functionality here, the ``factory`` used
+ to create route contexts may differ per-route as opposed to globally. See
+ the ``factory`` argument to :meth:`pyramid.config.Configurator.add_route`
+ for more info.
- Although we don't use the functionality here, the ``factory`` used
- to create route contexts may differ per-route as opposed to globally. See
- the ``factory`` argument to
- :meth:`pyramid.config.Configurator.add_route` for more info.
-
-Add Authentication and Authorization Policies
+Add authentication and authorization policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open ``tutorial/__init__.py`` and
-add these import statements:
+Open ``tutorial/tutorial/__init__.py`` and add the highlighted import
+statements:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 2-3,7
+ :lines: 1-7
:linenos:
+ :emphasize-lines: 2-3,7
:language: python
Now add those policies to the configuration:
@@ -143,17 +137,18 @@ Now add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 21-27
:linenos:
+ :lineno-start: 21
:emphasize-lines: 1-3,6-7
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added.
-We are enabling an ``AuthTktAuthenticationPolicy``, which is based in an
-auth ticket that may be included in the request.
-We are also enabling an ``ACLAuthorizationPolicy``, which uses an ACL to
-determine the *allow* or *deny* outcome for a view.
+We are enabling an ``AuthTktAuthenticationPolicy``, which is based in an auth
+ticket that may be included in the request. We are also enabling an
+``ACLAuthorizationPolicy``, which uses an ACL to determine the *allow* or
+*deny* outcome for a view.
-Note that the :class:`~pyramid.authentication.AuthTktAuthenticationPolicy`
+Note that the :class:`pyramid.authentication.AuthTktAuthenticationPolicy`
constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is
a string representing an encryption key used by the "authentication ticket"
machinery represented by this policy: it is required. The ``callback`` is the
@@ -161,50 +156,57 @@ machinery represented by this policy: it is required. The ``callback`` is the
Add permission declarations
~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Open ``tutorial/tutorial/views.py`` and add a ``permission='edit'`` parameter
+to the ``@view_config`` decorators for ``add_page()`` and ``edit_page()``:
-Add a ``permission='edit'`` parameter to the ``@view_config``
-decorator for ``add_page()`` and ``edit_page()``, for example:
-
-.. code-block:: python
- :linenos:
- :emphasize-lines: 2
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 60-61
+ :emphasize-lines: 1-2
+ :language: python
- @view_config(route_name='add_page', renderer='templates/edit.pt',
- permission='edit')
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 75-76
+ :emphasize-lines: 1-2
+ :language: python
-(Only the highlighted line needs to be added.)
+Only the highlighted lines, along with their preceding commas, need to be
+edited and added.
-The result is that only users who possess the ``edit``
-permission at the time of the request may invoke those two views.
+The result is that only users who possess the ``edit`` permission at the time
+of the request may invoke those two views.
-Add a ``permission='view'`` parameter to the ``@view_config``
-decorator for ``view_wiki()`` and ``view_page()``, like this:
+Add a ``permission='view'`` parameter to the ``@view_config`` decorator for
+``view_wiki()`` and ``view_page()`` as follows:
-.. code-block:: python
- :linenos:
- :emphasize-lines: 2
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 30-31
+ :emphasize-lines: 1-2
+ :language: python
- @view_config(route_name='view_page', renderer='templates/view.pt',
- permission='view')
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 36-37
+ :emphasize-lines: 1-2
+ :language: python
-(Only the highlighted line needs to be added.)
+Only the highlighted lines, along with their preceding commas, need to be
+edited and added.
This allows anyone to invoke these two views.
-We are done with the changes needed to control access. The
-changes that follow will add the login and logout feature.
+We are done with the changes needed to control access. The changes that
+follow will add the login and logout feature.
-Login, Logout
+Login, logout
-------------
Add routes for /login and /logout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Go back to ``tutorial/tutorial/__init__.py`` and add these two
-routes:
+Go back to ``tutorial/tutorial/__init__.py`` and add these two routes as
+highlighted:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 31-32
- :linenos:
+ :lines: 30-33
+ :emphasize-lines: 2-3
:language: python
.. note:: The preceding lines must be added *before* the following
@@ -212,202 +214,193 @@ routes:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 33
- :linenos:
:language: python
This is because ``view_page``'s route definition uses a catch-all
"replacement marker" ``/{pagename}`` (see :ref:`route_pattern_syntax`)
- which will catch any route that was not already caught by any
- route listed above it in ``__init__.py``. Hence, for ``login`` and
- ``logout`` views to have the opportunity of being matched
- (or "caught"), they must be above ``/{pagename}``.
+ which will catch any route that was not already caught by any route listed
+ above it in ``__init__.py``. Hence, for ``login`` and ``logout`` views to
+ have the opportunity of being matched (or "caught"), they must be above
+ ``/{pagename}``.
-Add Login and Logout Views
+Add login and logout views
~~~~~~~~~~~~~~~~~~~~~~~~~~
-We'll add a ``login`` view which renders a login form and processes
-the post from the login form, checking credentials.
+We'll add a ``login`` view which renders a login form and processes the post
+from the login form, checking credentials.
-We'll also add a ``logout`` view callable to our application and
-provide a link to it. This view will clear the credentials of the
-logged in user and redirect back to the front page.
+We'll also add a ``logout`` view callable to our application and provide a
+link to it. This view will clear the credentials of the logged in user and
+redirect back to the front page.
-Add the following import statements to the
-head of ``tutorial/tutorial/views.py``:
+Add the following import statements to the head of
+``tutorial/tutorial/views.py``:
.. literalinclude:: src/authorization/tutorial/views.py
:lines: 9-19
- :linenos:
- :emphasize-lines: 3,6-9,11
+ :emphasize-lines: 1-11
:language: python
-(Only the highlighted lines need to be added.)
+All the highlighted lines need to be added or edited.
-:meth:`~pyramid.view.forbidden_view_config` will be used
-to customize the default 403 Forbidden page.
-:meth:`~pyramid.security.remember` and
-:meth:`~pyramid.security.forget` help to create and
-expire an auth ticket cookie.
+:meth:`~pyramid.view.forbidden_view_config` will be used to customize the
+default 403 Forbidden page. :meth:`~pyramid.security.remember` and
+:meth:`~pyramid.security.forget` help to create and expire an auth ticket
+cookie.
-Now add the ``login`` and ``logout`` views:
+Now add the ``login`` and ``logout`` views at the end of the file:
.. literalinclude:: src/authorization/tutorial/views.py
:lines: 91-123
- :linenos:
:language: python
-``login()`` is decorated with two decorators:
+``login()`` has two decorators:
-- a ``@view_config`` decorator which associates it with the
- ``login`` route and makes it visible when we visit ``/login``,
-- a ``@forbidden_view_config`` decorator which turns it into
- an :term:`forbidden view`. ``login()`` will be invoked
- when a users tries to execute a view callable that
- they are not allowed to. For example, if a user has not logged in
- and tries to add or edit a Wiki page, he will be shown the
- login form before being allowed to continue on.
+- a ``@view_config`` decorator which associates it with the ``login`` route
+ and makes it visible when we visit ``/login``,
+- a ``@forbidden_view_config`` decorator which turns it into a
+ :term:`forbidden view`. ``login()`` will be invoked when a user tries to
+ execute a view callable for which they lack authorization. For example, if
+ a user has not logged in and tries to add or edit a Wiki page, they will be
+ shown the login form before being allowed to continue.
-The order of these two :term:`view configuration` decorators
-is unimportant.
+The order of these two :term:`view configuration` decorators is unimportant.
-``logout()`` is decorated with a ``@view_config`` decorator
-which associates it with the ``logout`` route. It will be
-invoked when we visit ``/logout``.
+``logout()`` is decorated with a ``@view_config`` decorator which associates
+it with the ``logout`` route. It will be invoked when we visit ``/logout``.
Add the ``login.pt`` Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Create ``tutorial/tutorial/templates/login.pt`` with the following
-content:
+Create ``tutorial/tutorial/templates/login.pt`` with the following content:
.. literalinclude:: src/authorization/tutorial/templates/login.pt
- :language: xml
+ :language: html
-The above template is referred to within the login view we just
-added to ``views.py``.
+The above template is referenced in the login view that we just added in
+``views.py``.
-Return a logged_in flag to the renderer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Return a ``logged_in`` flag to the renderer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Add a ``logged_in`` parameter to the return value of
-``view_page()``, ``edit_page()`` and ``add_page()``,
-like this:
+Open ``tutorial/tutorial/views.py`` again. Add a ``logged_in`` parameter to
+the return value of ``view_page()``, ``edit_page()``, and ``add_page()`` as
+follows:
-.. code-block:: python
- :linenos:
- :emphasize-lines: 4
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 57-58
+ :emphasize-lines: 1-2
+ :language: python
- return dict(page = page,
- content = content,
- edit_url = edit_url,
- logged_in = request.authenticated_userid)
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 72-73
+ :emphasize-lines: 1-2
+ :language: python
+
+.. literalinclude:: src/authorization/tutorial/views.py
+ :lines: 85-89
+ :emphasize-lines: 3-4
+ :language: python
-(Only the highlighted line needs to be added.)
+Only the highlighted lines need to be added or edited.
-The :meth:`~pyramid.request.Request.authenticated_userid` property will be
-``None`` if the user is not authenticated.
+The :meth:`pyramid.request.Request.authenticated_userid` will be ``None`` if
+the user is not authenticated, or a userid if the user is authenticated.
Add a "Logout" link when logged in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open ``tutorial/tutorial/templates/edit.pt`` and
-``tutorial/tutorial/templates/view.pt`` and add this within the
-``<div id="right" class="app-welcome align-right">`` div:
-
-.. code-block:: xml
+``tutorial/tutorial/templates/view.pt`` and add the following code as
+indicated by the highlighted lines.
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
+.. literalinclude:: src/authorization/tutorial/templates/edit.pt
+ :lines: 34-38
+ :emphasize-lines: 3-5
+ :language: html
-The attribute ``tal:condition="logged_in"`` will make the element be
-included when ``logged_in`` is any user id. The link will invoke
-the logout view. The above element will not be included if ``logged_in``
-is ``None``, such as when a user is not authenticated.
+The attribute ``tal:condition="logged_in"`` will make the element be included
+when ``logged_in`` is any user id. The link will invoke the logout view. The
+above element will not be included if ``logged_in`` is ``None``, such as when
+a user is not authenticated.
-Seeing Our Changes
-------------------
+Reviewing our changes
+---------------------
-Our ``tutorial/tutorial/__init__.py`` will look something like this
-when we're done:
+Our ``tutorial/tutorial/__init__.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
:emphasize-lines: 2-3,7,21-23,25-27,31-32
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/models.py`` will look something like this
-when we're done:
+Our ``tutorial/tutorial/models.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/models.py
:linenos:
:emphasize-lines: 1-4,33-37
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/views.py`` will look something like this
-when we're done:
+Our ``tutorial/tutorial/views.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/views.py
:linenos:
- :emphasize-lines: 11,14-19,25,31,37,58,61,73,76,88,91-117,119-123
+ :emphasize-lines: 9-11,14-19,25,31,37,58,61,73,76,88,91-117,119-123
:language: python
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/templates/edit.pt`` template will look
-something like this when we're done:
+Our ``tutorial/tutorial/templates/edit.pt`` template will look like this when
+we're done:
.. literalinclude:: src/authorization/tutorial/templates/edit.pt
:linenos:
- :emphasize-lines: 41-43
- :language: xml
+ :emphasize-lines: 36-38
+ :language: html
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/templates/view.pt`` template will look
-something like this when we're done:
+Our ``tutorial/tutorial/templates/view.pt`` template will look like this when
+we're done:
.. literalinclude:: src/authorization/tutorial/templates/view.pt
:linenos:
- :emphasize-lines: 41-43
- :language: xml
+ :emphasize-lines: 36-38
+ :language: html
-(Only the highlighted lines need to be added.)
+Only the highlighted lines need to be added or edited.
-Viewing the Application in a Browser
+Viewing the application in a browser
------------------------------------
We can finally examine our application in a browser (See
-:ref:`wiki2-start-the-application`). Launch a browser and visit
-each of the following URLs, check that the result is as expected:
-
-- http://localhost:6543/ invokes the
- ``view_wiki`` view. This always redirects to the ``view_page`` view
- of the FrontPage page object. It is executable by any user.
-
-- http://localhost:6543/FrontPage invokes
- the ``view_page`` view of the FrontPage page object.
-
-- http://localhost:6543/FrontPage/edit_page
- invokes the edit view for the FrontPage object. It is executable by
- only the ``editor`` user. If a different user (or the anonymous
- user) invokes it, a login form will be displayed. Supplying the
- credentials with the username ``editor``, password ``editor`` will
- display the edit page form.
-
-- http://localhost:6543/add_page/SomePageName
- invokes the add view for a page. It is executable by only
- the ``editor`` user. If a different user (or the anonymous user)
- invokes it, a login form will be displayed. Supplying the
- credentials with the username ``editor``, password ``editor`` will
- display the edit page form.
-
-- After logging in (as a result of hitting an edit or add page
- and submitting the login form with the ``editor``
- credentials), we'll see a Logout link in the upper right hand
- corner. When we click it, we're logged out, and redirected
- back to the front page.
+:ref:`wiki2-start-the-application`). Launch a browser and visit each of the
+following URLs, checking that the result is as expected:
+
+- http://localhost:6543/ invokes the ``view_wiki`` view. This always
+ redirects to the ``view_page`` view of the ``FrontPage`` page object. It
+ is executable by any user.
+
+- http://localhost:6543/FrontPage invokes the ``view_page`` view of the
+ ``FrontPage`` page object.
+
+- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
+ FrontPage object. It is executable by only the ``editor`` user. If a
+ different user (or the anonymous user) invokes it, a login form will be
+ displayed. Supplying the credentials with the username ``editor``, password
+ ``editor`` will display the edit page form.
+
+- http://localhost:6543/add_page/SomePageName invokes the add view for a page.
+ It is executable by only the ``editor`` user. If a different user (or the
+ anonymous user) invokes it, a login form will be displayed. Supplying the
+ credentials with the username ``editor``, password ``editor`` will display
+ the edit page form.
+
+- After logging in (as a result of hitting an edit or add page and submitting
+ the login form with the ``editor`` credentials), we'll see a Logout link in
+ the upper right hand corner. When we click it, we're logged out, and
+ redirected back to the front page.
diff --git a/docs/tutorials/wiki2/background.rst b/docs/tutorials/wiki2/background.rst
index 1f9582903..b8afb8305 100644
--- a/docs/tutorials/wiki2/background.rst
+++ b/docs/tutorials/wiki2/background.rst
@@ -2,10 +2,12 @@
Background
==========
-This tutorial presents a :app:`Pyramid` application that uses technologies
-which will be familiar to someone with SQL database experience. It uses
+This version of the :app:`Pyramid` wiki tutorial presents a
+:app:`Pyramid` application that uses technologies which will be
+familiar to someone with SQL database experience. It uses
:term:`SQLAlchemy` as a persistence mechanism and :term:`url dispatch` to map
-URLs to code.
+URLs to code. It can also be followed by people without any prior
+Python web framework experience.
To code along with this tutorial, the developer will need a UNIX
machine with development tools (Mac OS X with XCode, any Linux or BSD
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index 05781c044..80ae4b34e 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -4,16 +4,17 @@ Basic Layout
The starter files generated by the ``alchemy`` scaffold are very basic, but
they provide a good orientation for the high-level patterns common to most
-:term:`url dispatch` -based :app:`Pyramid` projects.
+:term:`URL dispatch`-based :app:`Pyramid` projects.
-Application Configuration with ``__init__.py``
+Application configuration with ``__init__.py``
----------------------------------------------
A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file. Even if empty, this marks a directory as a Python
-package. We use ``__init__.py`` both as a marker indicating the directory
-it's contained within is a package, and to contain configuration code.
+package. We use ``__init__.py`` both as a marker, indicating the directory
+in which it's contained is a package, and to contain application configuration
+code.
Open ``tutorial/tutorial/__init__.py``. It should already contain
the following:
@@ -114,7 +115,7 @@ used when the URL is ``/``:
:lines: 19
:language: py
-Since this route has a ``pattern`` equalling ``/`` it is the route that will
+Since this route has a ``pattern`` equaling ``/`` it is the route that will
be matched when the URL ``/`` is visited, e.g. ``http://localhost:6543/``.
``main`` next calls the ``scan`` method of the configurator
@@ -136,7 +137,7 @@ Finally, ``main`` is finished configuring things, so it uses the
:lines: 21
:language: py
-View Declarations via ``views.py``
+View declarations via ``views.py``
----------------------------------
The main function of a web framework is mapping each URL pattern to code (a
@@ -167,7 +168,7 @@ Note that ``my_view()`` accepts a single argument named ``request``. This is
the standard call signature for a Pyramid :term:`view callable`.
Remember in our ``__init__.py`` when we executed the
-:meth:`pyramid.config.Configurator.scan` method, i.e. ``config.scan()``? The
+:meth:`pyramid.config.Configurator.scan` method ``config.scan()``? The
purpose of calling the scan method was to find and process this
``@view_config`` decorator in order to create a view configuration within our
application. Without being processed by ``scan``, the decorator effectively
@@ -175,7 +176,7 @@ does nothing. ``@view_config`` is inert without being detected via a
:term:`scan`.
The sample ``my_view()`` created by the scaffold uses a ``try:`` and ``except:``
-clause, to detect if there is a problem accessing the project database and
+clause to detect if there is a problem accessing the project database and
provide an alternate error response. That response will include the text
shown at the end of the file, which will be displayed in the browser to
inform the user about possible actions to take to solve the problem.
@@ -203,7 +204,7 @@ Let's examine this in detail. First, we need some imports to support later code:
Next we set up a SQLAlchemy ``DBSession`` object:
.. literalinclude:: src/basiclayout/tutorial/models.py
- :lines: 16
+ :lines: 17
:language: py
``scoped_session`` and ``sessionmaker`` are standard SQLAlchemy helpers.
@@ -246,5 +247,8 @@ The ``MyModel`` class has a ``__tablename__`` attribute. This informs
SQLAlchemy which table to use to store the data representing instances of this
class.
+The Index import and the Index object creation is not required for this
+tutorial, and will be removed in the next step.
+
That's about all there is to it regarding models, views, and initialization
code in our stock application.
diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst
index e30af12b2..b2d9bf83a 100644
--- a/docs/tutorials/wiki2/definingmodels.rst
+++ b/docs/tutorials/wiki2/definingmodels.rst
@@ -7,26 +7,27 @@ be to define a :term:`domain model` constructor representing a wiki page.
We'll do this inside our ``models.py`` file.
-Making Edits to ``models.py``
------------------------------
+Edit ``models.py``
+------------------
.. note::
There is nothing special about the filename ``models.py``. A
- project may have many models throughout its codebase in arbitrarily-named
+ project may have many models throughout its codebase in arbitrarily named
files. Files implementing models often have ``model`` in their filenames
- (or they may live in a Python subpackage of your application package named
- ``models``) , but this is only by convention.
+ or they may live in a Python subpackage of your application package named
+ ``models``, but this is only by convention.
-Open ``tutorial/tutorial/models.py`` file and edit it to look like the
+Open ``tutorial/tutorial/models.py`` file and edit it to look like the
following:
.. literalinclude:: src/models/tutorial/models.py
:linenos:
:language: py
- :emphasize-lines: 20-22,25
+ :emphasize-lines: 20-22,24,25
-(The highlighted lines are the ones that need to be changed.)
+The highlighted lines are the ones that need to be changed, as well as
+removing lines that reference ``Index``.
The first thing we've done is remove the stock ``MyModel`` class
from the generated ``models.py`` file. The ``MyModel`` class is only a
@@ -44,74 +45,74 @@ this class inherits from an instance of
As you can see, our ``Page`` class has a class level attribute
``__tablename__`` which equals the string ``'pages'``. This means that
SQLAlchemy will store our wiki data in a SQL table named ``pages``. Our
-``Page`` class will also have class-level attributes named ``id``, ``name`` and
-``data`` (all instances of :class:`sqlalchemy.schema.Column`).
-These will map to columns in the ``pages`` table.
-The ``id`` attribute will be the primary key in the table.
-The ``name`` attribute will be a text attribute, each value of
-which needs to be unique within the column. The ``data`` attribute is a text
-attribute that will hold the body of each page.
+``Page`` class will also have class-level attributes named ``id``, ``name``
+and ``data`` (all instances of :class:`sqlalchemy.schema.Column`). These will
+map to columns in the ``pages`` table. The ``id`` attribute will be the
+primary key in the table. The ``name`` attribute will be a text attribute,
+each value of which needs to be unique within the column. The ``data``
+attribute is a text attribute that will hold the body of each page.
Changing ``scripts/initializedb.py``
------------------------------------
We haven't looked at the details of this file yet, but within the ``scripts``
-directory of your ``tutorial`` package is a file named ``initializedb.py``. Code
-in this file is executed whenever we run the ``initialize_tutorial_db`` command
-(as we did in the installation step of this tutorial).
+directory of your ``tutorial`` package is a file named ``initializedb.py``.
+Code in this file is executed whenever we run the ``initialize_tutorial_db``
+command, as we did in the installation step of this tutorial.
-Since we've changed our model, we need to make changes to our ``initializedb.py``
-script. In particular, we'll replace our import of ``MyModel`` with one of
-``Page`` and we'll change the very end of the script to create a ``Page``
-rather than a ``MyModel`` and add it to our ``DBSession``.
+Since we've changed our model, we need to make changes to our
+``initializedb.py`` script. In particular, we'll replace our import of
+``MyModel`` with one of ``Page`` and we'll change the very end of the script
+to create a ``Page`` rather than a ``MyModel`` and add it to our
+``DBSession``.
-Open ``tutorial/tutorial/scripts/initializedb.py`` and edit it to look like the
-following:
+Open ``tutorial/tutorial/scripts/initializedb.py`` and edit it to look like
+the following:
.. literalinclude:: src/models/tutorial/scripts/initializedb.py
:linenos:
:language: python
- :emphasize-lines: 14,36
+ :emphasize-lines: 14,31,36
-(Only the highlighted lines need to be changed.)
+Only the highlighted lines need to be changed, as well as removing the lines
+referencing ``pyramid.scripts.common`` and ``options`` under the ``main``
+function.
-Installing the Project and re-initializing the Database
+Installing the project and re-initializing the database
-------------------------------------------------------
Because our model has changed, in order to reinitialize the database, we need
-to rerun the ``initialize_tutorial_db`` command to pick up the changes you've made
-to both the models.py file and to the initializedb.py file.
-See :ref:`initialize_db_wiki2` for instructions.
+to rerun the ``initialize_tutorial_db`` command to pick up the changes you've
+made to both the models.py file and to the initializedb.py file. See
+:ref:`initialize_db_wiki2` for instructions.
Success will look something like this::
- 2011-11-27 01:22:45,277 INFO [sqlalchemy.engine.base.Engine][MainThread]
- PRAGMA table_info("pages")
- 2011-11-27 01:22:45,277 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
- 2011-11-27 01:22:45,277 INFO [sqlalchemy.engine.base.Engine][MainThread]
- CREATE TABLE pages (
- id INTEGER NOT NULL,
- name TEXT,
- data TEXT,
- PRIMARY KEY (id),
- UNIQUE (name)
- )
-
-
- 2011-11-27 01:22:45,278 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
- 2011-11-27 01:22:45,397 INFO [sqlalchemy.engine.base.Engine][MainThread]
- COMMIT
- 2011-11-27 01:22:45,400 INFO [sqlalchemy.engine.base.Engine][MainThread]
- BEGIN (implicit)
- 2011-11-27 01:22:45,401 INFO [sqlalchemy.engine.base.Engine][MainThread]
- INSERT INTO pages (name, data) VALUES (?, ?)
- 2011-11-27 01:22:45,401 INFO [sqlalchemy.engine.base.Engine][MainThread]
- ('FrontPage', 'This is the front page')
- 2011-11-27 01:22:45,402 INFO [sqlalchemy.engine.base.Engine][MainThread]
- COMMIT
-
-Viewing the Application in a Browser
-------------------------------------
+ 2015-05-24 15:34:14,542 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
+ 2015-05-24 15:34:14,542 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] ()
+ 2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
+ 2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] ()
+ 2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages")
+ 2015-05-24 15:34:14,544 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
+ 2015-05-24 15:34:14,544 INFO [sqlalchemy.engine.base.Engine:1097][MainThread]
+ CREATE TABLE pages (
+ id INTEGER NOT NULL,
+ name TEXT,
+ data TEXT,
+ PRIMARY KEY (id),
+ UNIQUE (name)
+ )
+
+
+ 2015-05-24 15:34:14,545 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
+ 2015-05-24 15:34:14,546 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
+ 2015-05-24 15:34:14,548 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit)
+ 2015-05-24 15:34:14,549 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data) VALUES (?, ?)
+ 2015-05-24 15:34:14,549 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page')
+ 2015-05-24 15:34:14,550 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
+
+View the application in a browser
+---------------------------------
We can't. At this point, our system is in a "non-runnable" state; we'll need
to change view-related files in the next chapter to be able to start the
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index 49dbed50f..0b495445a 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -11,8 +11,8 @@ The request object has a dictionary as an attribute named ``matchdict``. A
substrings of the path in the :term:`request` URL. For instance, if a call to
:meth:`pyramid.config.Configurator.add_route` has the pattern ``/{one}/{two}``,
and a user visits ``http://example.com/foo/bar``, our pattern would be matched
-against ``/foo/bar`` and the ``matchdict`` would look like: ``{'one':'foo',
-'two':'bar'}``
+against ``/foo/bar`` and the ``matchdict`` would look like ``{'one':'foo',
+'two':'bar'}``.
Declaring Dependencies in Our ``setup.py`` File
===============================================
@@ -23,26 +23,27 @@ application was generated by the ``pcreate`` command; it doesn't know
about our custom application requirements.
We need to add a dependency on the ``docutils`` package to our ``tutorial``
-package's ``setup.py`` file by assigning this dependency to the ``requires`` parameter in ``setup()``.
+package's ``setup.py`` file by assigning this dependency to the ``requires``
+parameter in the ``setup()`` function.
Open ``tutorial/setup.py`` and edit it to look like the following:
.. literalinclude:: src/views/setup.py
:linenos:
- :language: python
:emphasize-lines: 20
+ :language: python
-(Only the highlighted line needs to be added.)
+Only the highlighted line needs to be added.
Running ``setup.py develop``
============================
-Since a new software dependency was added, you will need to rerun ``python
-setup.py develop`` inside the root of the ``tutorial`` package to obtain and
-register the newly added dependency distribution.
+Since a new software dependency was added, you will need to run ``python
+setup.py develop`` again inside the root of the ``tutorial`` package to obtain
+and register the newly added dependency distribution.
Make sure your current working directory is the root of the project (the
-directory in which setup.py lives) and execute the following command.
+directory in which ``setup.py`` lives) and execute the following command.
On UNIX:
@@ -63,21 +64,24 @@ like::
Finished processing dependencies for tutorial==0.0
-Changing the ``views.py`` File
-==============================
+Adding view functions in ``views.py``
+=====================================
-It's time for a major change. Open ``tutorial/tutorial/views.py`` and edit it to look like the following:
+It's time for a major change. Open ``tutorial/tutorial/views.py`` and edit it
+to look like the following:
.. literalinclude:: src/views/tutorial/views.py
:linenos:
:language: python
- :emphasize-lines: 1-7,12,15-70
+ :emphasize-lines: 1-7,14,16-72
-(The highlighted lines are the ones that need to be added or edited.)
+The highlighted lines need to be added or edited.
-We got rid of the ``my_view`` view function and its decorator that was
-added when we originally rendered the ``alchemy`` scaffold. It was only an
-example and isn't relevant to our application.
+We added some imports and created a regular expression to find "WikiWords".
+
+We got rid of the ``my_view`` view function and its decorator that was added
+when we originally rendered the ``alchemy`` scaffold. It was only an example
+and isn't relevant to our application.
Then we added four :term:`view callable` functions to our ``views.py``
module:
@@ -87,13 +91,12 @@ module:
* ``add_page()`` - Allows the user to add a page.
* ``edit_page()`` - Allows the user to edit a page.
-We'll describe each one briefly and show the resulting ``views.py`` file
-afterward.
+We'll describe each one briefly in the following sections.
.. note::
There is nothing special about the filename ``views.py``. A project may
- have many view callables throughout its codebase in arbitrarily-named
+ have many view callables throughout its codebase in arbitrarily named
files. Files implementing view callables often have ``view`` in their
filenames (or may live in a Python subpackage of your application package
named ``views``), but this is only by convention.
@@ -101,39 +104,44 @@ afterward.
The ``view_wiki`` view function
-------------------------------
-``view_wiki()`` is the :term:`default view` that gets called when a request
-is made to the root URL of our wiki. It always redirects to
-a URL which represents the path to our "FrontPage".
+Following is the code for the ``view_wiki`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views.py
- :lines: 18-21
+ :lines: 20-24
+ :lineno-start: 20
:linenos:
:language: python
-``view_wiki()`` returns an instance of the
-:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
-the :class:`pyramid.interfaces.IResponse` interface like
-:class:`pyramid.response.Response` does).
+``view_wiki()`` is the :term:`default view` that gets called when a request is
+made to the root URL of our wiki. It always redirects to an URL which
+represents the path to our "FrontPage".
-It uses the :meth:`pyramid.request.Request.route_url` API to construct a
-URL to the ``FrontPage`` page (e.g. ``http://localhost:6543/FrontPage``), which
-is used as the "location" of the ``HTTPFound`` response, forming an HTTP redirect.
+The ``view_wiki`` view callable always redirects to the URL of a Page resource
+named "FrontPage". To do so, it returns an instance of the
+:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
+the :class:`pyramid.interfaces.IResponse` interface, like
+:class:`pyramid.response.Response` does). It uses the
+:meth:`pyramid.request.Request.route_url` API to construct an URL to the
+``FrontPage`` page (i.e., ``http://localhost:6543/FrontPage``), and uses it as
+the "location" of the ``HTTPFound`` response, forming an HTTP redirect.
The ``view_page`` view function
-------------------------------
-``view_page()`` is used to display a single page of our
-wiki. It renders the :term:`reStructuredText` body of a page (stored as
-the ``data`` attribute of a ``Page`` model object) as HTML. Then it substitutes an
-HTML anchor for each *WikiWord* reference in the rendered HTML using a
-compiled regular expression.
+Here is the code for the ``view_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views.py
- :lines: 23-43
+ :lines: 25-45
+ :lineno-start: 25
:linenos:
:language: python
-The ``check()`` function is used as the first argument to
+``view_page()`` is used to display a single page of our wiki. It renders the
+:term:`reStructuredText` body of a page (stored as the ``data`` attribute of a
+``Page`` model object) as HTML. Then it substitutes an HTML anchor for each
+*WikiWord* reference in the rendered HTML using a compiled regular expression.
+
+The curried function named ``check`` is used as the first argument to
``wikiwords.sub``, indicating that it should be called to provide a value for
each WikiWord match found in the content. If the wiki already contains a
page with the matched WikiWord name, ``check()`` generates a view
@@ -156,6 +164,14 @@ renderer used will be the ``templates/view.pt`` template, as indicated in the
The ``add_page`` view function
------------------------------
+Here is the code for the ``add_page`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :lines: 47-58
+ :lineno-start: 47
+ :linenos:
+ :language: python
+
``add_page()`` is invoked when a user clicks on a *WikiWord* which
isn't yet represented as a page in the system. The ``check`` function
within the ``view_page`` view generates URLs to this view.
@@ -164,26 +180,21 @@ when we want to add a page object. The ``matchdict`` attribute of the
request passed to the ``add_page()`` view will have the values we need
to construct URLs and find model objects.
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 45-56
- :linenos:
- :language: python
-
The ``matchdict`` will have a ``'pagename'`` key that matches the name of
the page we'd like to add. If our add view is invoked via,
-e.g. ``http://localhost:6543/add_page/SomeName``, the value for
+e.g., ``http://localhost:6543/add_page/SomeName``, the value for
``'pagename'`` in the ``matchdict`` will be ``'SomeName'``.
-If the view execution *is* a result of a form submission (i.e. the expression
-``'form.submitted' in request.params`` is ``True``), we scrape the page body
+If the view execution *is* a result of a form submission (i.e., the expression
+``'form.submitted' in request.params`` is ``True``), we grab the page body
from the form data, create a Page object with this page body and the name
taken from ``matchdict['pagename']``, and save it into the database using
``DBSession.add``. We then redirect back to the ``view_page`` view for the
newly created page.
-If the view execution is *not* a result of a form submission (i.e. the
+If the view execution is *not* a result of a form submission (i.e., the
expression ``'form.submitted' in request.params`` is ``False``), the view
-callable renders a template. To do so, it generates a "save url" which the
+callable renders a template. To do so, it generates a ``save_url`` which the
template uses as the form post URL during rendering. We're lazy here, so
we're going to use the same template (``templates/edit.pt``) for the add
view as well as the page edit view. To do so we create a dummy Page object
@@ -194,38 +205,41 @@ with this view to a response.
The ``edit_page`` view function
-------------------------------
+Here is the code for the ``edit_page`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :lines: 60-72
+ :lineno-start: 60
+ :linenos:
+ :language: python
+
``edit_page()`` is invoked when a user clicks the "Edit this
Page" button on the view form. It renders an edit form but it also acts as
the handler for the form it renders. The ``matchdict`` attribute of the
request passed to the ``edit_page`` view will have a ``'pagename'`` key
matching the name of the page the user wants to edit.
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 58-70
- :linenos:
- :language: python
-
-If the view execution *is* a result of a form submission (i.e. the expression
+If the view execution *is* a result of a form submission (i.e., the expression
``'form.submitted' in request.params`` is ``True``), the view grabs the
``body`` element of the request parameters and sets it as the ``data``
attribute of the page object. It then redirects to the ``view_page`` view
of the wiki page.
-If the view execution is *not* a result of a form submission (i.e. the
+If the view execution is *not* a result of a form submission (i.e., the
expression ``'form.submitted' in request.params`` is ``False``), the view
simply renders the edit form, passing the page object and a ``save_url``
which will be used as the action of the generated form.
-Adding Templates
+Adding templates
================
The ``view_page``, ``add_page`` and ``edit_page`` views that we've added
-reference a :term:`template`. Each template is a :term:`Chameleon` :term:`ZPT`
-template. These templates will live in the ``templates`` directory of our
-tutorial package. Chameleon templates must have a ``.pt`` extension to be
-recognized as such.
+reference a :term:`template`. Each template is a :term:`Chameleon`
+:term:`ZPT` template. These templates will live in the ``templates``
+directory of our tutorial package. Chameleon templates must have a ``.pt``
+extension to be recognized as such.
-The ``view.pt`` Template
+The ``view.pt`` template
------------------------
Create ``tutorial/tutorial/templates/view.pt`` and add the following
@@ -233,20 +247,18 @@ content:
.. literalinclude:: src/views/tutorial/templates/view.pt
:linenos:
- :language: xml
+ :language: html
This template is used by ``view_page()`` for displaying a single
wiki page. It includes:
-- A ``div`` element that is replaced with the ``content``
- value provided by the view (rows 45-47). ``content``
- contains HTML, so the ``structure`` keyword is used
- to prevent escaping it (i.e. changing ">" to "&gt;", etc.)
-- A link that points
- at the "edit" URL which invokes the ``edit_page`` view for
- the page being viewed (rows 49-51).
+- A ``div`` element that is replaced with the ``content`` value provided by
+ the view (lines 36-38). ``content`` contains HTML, so the ``structure``
+ keyword is used to prevent escaping it (i.e., changing ">" to "&gt;", etc.)
+- A link that points at the "edit" URL which invokes the ``edit_page`` view
+ for the page being viewed (lines 40-42).
-The ``edit.pt`` Template
+The ``edit.pt`` template
------------------------
Create ``tutorial/tutorial/templates/edit.pt`` and add the following
@@ -254,44 +266,38 @@ content:
.. literalinclude:: src/views/tutorial/templates/edit.pt
:linenos:
- :language: xml
+ :language: html
-This template is used by ``add_page()`` and ``edit_page()`` for adding
-and editing a wiki page. It displays
-a page containing a form that includes:
+This template is used by ``add_page()`` and ``edit_page()`` for adding and
+editing a wiki page. It displays a page containing a form that includes:
- A 10 row by 60 column ``textarea`` field named ``body`` that is filled
- with any existing page data when it is rendered (rows 46-47).
-- A submit button that has the name ``form.submitted`` (row 48).
+ with any existing page data when it is rendered (line 45).
+- A submit button that has the name ``form.submitted`` (line 48).
-The form POSTs back to the "save_url" argument supplied
-by the view (row 45). The view will use the ``body`` and
-``form.submitted`` values.
+The form POSTs back to the ``save_url`` argument supplied by the view (line
+43). The view will use the ``body`` and ``form.submitted`` values.
-.. note:: Our templates use a ``request`` object that
- none of our tutorial views return in their dictionary.
- ``request`` is one of several
- names that are available "by default" in a template when a template
- renderer is used. See :ref:`renderer_system_values` for
- information about other names that are available by default
- when a template is used as a renderer.
+.. note:: Our templates use a ``request`` object that none of our tutorial
+ views return in their dictionary. ``request`` is one of several names that
+ are available "by default" in a template when a template renderer is used.
+ See :ref:`renderer_system_values` for information about other names that
+ are available by default when a template is used as a renderer.
Static Assets
-------------
-Our templates name a single static asset named ``pylons.css``. We don't need
-to create this file within our package's ``static`` directory because it was
-provided at the time we created the project. This file is a little too long
-to replicate within the body of this guide, however it is available `online
-<https://github.com/Pylons/pyramid/blob/master/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css>`_.
+Our templates name static assets, including CSS and images. We don't need
+to create these files within our package's ``static`` directory because they
+were provided at the time we created the project.
-This CSS file will be accessed via
-e.g. ``http://localhost:6543/static/pylons.css`` by virtue of the call to
+As an example, the CSS file will be accessed via
+``http://localhost:6543/static/theme.css`` by virtue of the call to the
``add_static_view`` directive we've made in the ``__init__.py`` file. Any
number and type of static assets can be placed in this directory (or
subdirectories) and are just referred to by URL or by using the convenience
-method ``static_url``
-e.g. ``request.static_url('{{package}}:static/foo.css')`` within templates.
+method ``static_url``, e.g.,
+``request.static_url('<package>:static/foo.css')`` within templates.
Adding Routes to ``__init__.py``
================================
@@ -334,33 +340,30 @@ something like:
.. literalinclude:: src/views/tutorial/__init__.py
:linenos:
- :language: python
:emphasize-lines: 19-22
+ :language: python
-(The highlighted lines are the ones that need to be added or edited.)
+The highlighted lines are the ones that need to be added or edited.
-Viewing the Application in a Browser
+Viewing the application in a browser
====================================
We can finally examine our application in a browser (See
:ref:`wiki2-start-the-application`). Launch a browser and visit
-each of the following URLs, check that the result is as expected:
+each of the following URLs, checking that the result is as expected:
-- http://localhost:6543 in a browser invokes the
- ``view_wiki`` view. This always redirects to the ``view_page`` view
- of the FrontPage page object.
+- http://localhost:6543/ invokes the ``view_wiki`` view. This always
+ redirects to the ``view_page`` view of the ``FrontPage`` page object.
-- http://localhost:6543/FrontPage in a browser invokes
- the ``view_page`` view of the front page object.
+- http://localhost:6543/FrontPage invokes the ``view_page`` view of the front
+ page object.
-- http://localhost:6543/FrontPage/edit_page in a browser
- invokes the edit view for the front page object.
+- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
+ front page object.
-- http://localhost:6543/add_page/SomePageName in a
- browser invokes the add view for a page.
+- http://localhost:6543/add_page/SomePageName invokes the add view for a page.
- To generate an error, visit http://localhost:6543/foobars/edit_page which
- will generate a ``NoResultFound: No row was found for one()`` error.
- You'll see an interactive traceback facility provided
- by :term:`pyramid_debugtoolbar`.
-
+ will generate a ``NoResultFound: No row was found for one()`` error. You'll
+ see an interactive traceback facility provided by
+ :term:`pyramid_debugtoolbar`.
diff --git a/docs/tutorials/wiki2/design.rst b/docs/tutorials/wiki2/design.rst
index ff7413668..e9f361e7d 100644
--- a/docs/tutorials/wiki2/design.rst
+++ b/docs/tutorials/wiki2/design.rst
@@ -9,7 +9,7 @@ tutorial.
Overall
-------
-We choose to use :term:`reStructuredText` markup in the wiki text. Translation
+We choose to use :term:`reStructuredText` markup in the wiki text. Translation
from reStructuredText to HTML is provided by the widely used ``docutils``
Python module. We will add this module in the dependency list on the project
``setup.py`` file.
@@ -37,8 +37,8 @@ Views
-----
There will be three views to handle the normal operations of adding,
-editing and viewing wiki pages, plus one view for the wiki front page.
-Two templates will be used, one for viewing, and one for both for adding
+editing, and viewing wiki pages, plus one view for the wiki front page.
+Two templates will be used, one for viewing, and one for both adding
and editing wiki pages.
The default templating systems in :app:`Pyramid` are
@@ -53,13 +53,14 @@ Security
We'll eventually be adding security to our application. The components we'll
use to do this are below.
-- USERS, a dictionary mapping users names (the user's :term:`userids
- <userid>`) to their corresponding passwords.
+- USERS, a dictionary mapping :term:`userids <userid>` to their
+ corresponding passwords.
-- GROUPS, a dictionary mapping user names to a list of groups they belong to.
+- GROUPS, a dictionary mapping :term:`userids <userid>` to a
+ list of groups to which they belong.
- ``groupfinder``, an *authorization callback* that looks up USERS and
- GROUPS. It will be provided in a new *security.py* file.
+ GROUPS. It will be provided in a new ``security.py`` file.
- An :term:`ACL` is attached to the root :term:`resource`. Each row below
details an :term:`ACE`:
@@ -101,7 +102,7 @@ listed in the following table:
| | with existing | | | |
| | content. | | | |
| | | | | |
-| | If the form is | | | |
+| | If the form was | | | |
| | submitted, redirect | | | |
| | to /PageName | | | |
+----------------------+-----------------------+-------------+------------+------------+
@@ -111,7 +112,7 @@ listed in the following table:
| | the edit form | | | |
| | without content. | | | |
| | | | | |
-| | If the form is | | | |
+| | If the form was | | | |
| | submitted, | | | |
| | redirect to | | | |
| | /PageName | | | |
@@ -119,12 +120,12 @@ listed in the following table:
| /login | Display login form, | login | login.pt | |
| | Forbidden [3]_ | | | |
| | | | | |
-| | If the form is | | | |
+| | If the form was | | | |
| | submitted, | | | |
| | authenticate. | | | |
| | | | | |
| | - If authentication | | | |
-| | successful, | | | |
+| | succeeds, | | | |
| | redirect to the | | | |
| | page that we | | | |
| | came from. | | | |
@@ -144,6 +145,6 @@ listed in the following table:
when there is no view name.
.. [2] Pyramid will return a default 404 Not Found page
if the page *PageName* does not exist yet.
-.. [3] pyramid.exceptions.Forbidden is reached when a
+.. [3] ``pyramid.exceptions.Forbidden`` is reached when a
user tries to invoke a view that is
not authorized by the authorization policy.
diff --git a/docs/tutorials/wiki2/distributing.rst b/docs/tutorials/wiki2/distributing.rst
index 3b048a141..fee50a1cf 100644
--- a/docs/tutorials/wiki2/distributing.rst
+++ b/docs/tutorials/wiki2/distributing.rst
@@ -2,11 +2,11 @@
Distributing Your Application
=============================
-Once your application works properly, you can create a "tarball" from
-it by using the ``setup.py sdist`` command. The following commands
-assume your current working directory is the ``tutorial`` package
-we've created and that the parent directory of the ``tutorial``
-package is a virtualenv representing a :app:`Pyramid` environment.
+Once your application works properly, you can create a "tarball" from it by
+using the ``setup.py sdist`` command. The following commands assume your
+current working directory is the ``tutorial`` package we've created and that
+the parent directory of the ``tutorial`` package is a virtualenv representing
+a :app:`Pyramid` environment.
On UNIX:
@@ -25,18 +25,16 @@ The output of such a command will be something like:
.. code-block:: text
running sdist
- # ... more output ...
+ # .. more output ..
creating dist
tar -cf dist/tutorial-0.0.tar tutorial-0.0
gzip -f9 dist/tutorial-0.0.tar
removing 'tutorial-0.0' (and everything under it)
-Note that this command creates a tarball in the "dist" subdirectory
-named ``tutorial-0.0.tar.gz``. You can send this file to your friends
-to show them your cool new application. They should be able to
-install it by pointing the ``easy_install`` command directly at it.
-Or you can upload it to `PyPI <http://pypi.python.org>`_ and share it
-with the rest of the world, where it can be downloaded via
-``easy_install`` remotely like any other package people download from
-PyPI.
-
+Note that this command creates a tarball in the "dist" subdirectory named
+``tutorial-0.0.tar.gz``. You can send this file to your friends to show them
+your cool new application. They should be able to install it by pointing the
+``easy_install`` command directly at it. Or you can upload it to `PyPI
+<http://pypi.python.org>`_ and share it with the rest of the world, where it
+can be downloaded via ``easy_install`` remotely like any other package people
+download from PyPI.
diff --git a/docs/tutorials/wiki2/index.rst b/docs/tutorials/wiki2/index.rst
index 0a614cb23..0a3873dcd 100644
--- a/docs/tutorials/wiki2/index.rst
+++ b/docs/tutorials/wiki2/index.rst
@@ -3,7 +3,7 @@
SQLAlchemy + URL Dispatch Wiki Tutorial
=======================================
-This tutorial introduces a :term:`SQLAlchemy` and :term:`url dispatch` -based
+This tutorial introduces a :term:`SQLAlchemy` and :term:`url dispatch`-based
:app:`Pyramid` application to a developer familiar with Python. When the
tutorial is finished, the developer will have created a basic Wiki
application with authentication.
@@ -26,4 +26,3 @@ which corresponds to the same location if you have Pyramid sources.
tests
distributing
-
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index e21bf7108..1385ab8c7 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -2,22 +2,41 @@
Installation
============
-Before You Begin
+Before you begin
================
This tutorial assumes that you have already followed the steps in
-:ref:`installing_chapter`, thereby satisfying the following
-requirements.
+:ref:`installing_chapter`, except **do not create a virtualenv or install
+Pyramid**. Thereby you will satisfy the following requirements.
* Python interpreter is installed on your operating system
* :term:`setuptools` or :term:`distribute` is installed
* :term:`virtualenv` is installed
-Create and Use a Virtual Python Environment
+Create directory to contain the project
+---------------------------------------
+
+We need a workspace for our project files.
+
+On UNIX
+^^^^^^^
+
+.. code-block:: text
+
+ $ mkdir ~/pyramidtut
+
+On Windows
+^^^^^^^^^^
+
+.. code-block:: text
+
+ c:\> mkdir pyramidtut
+
+Create and use a virtual Python environment
-------------------------------------------
Next let's create a `virtualenv` workspace for our project. We will
-use the `VENV` environment variable instead of absolute path of the
+use the `VENV` environment variable instead of the absolute path of the
virtual environment.
On UNIX
@@ -33,8 +52,6 @@ On UNIX
On Windows
^^^^^^^^^^
-Set the `VENV` environment variable.
-
.. code-block:: text
c:\> set VENV=c:\pyramidtut
@@ -54,7 +71,7 @@ Python 3.2:
c:\> c:\Python32\Scripts\virtualenv %VENV%
-Install Pyramid Into the Virtual Python Environment
+Install Pyramid into the virtual Python environment
---------------------------------------------------
On UNIX
@@ -69,9 +86,9 @@ On Windows
.. code-block:: text
- c:\env> %VENV%\Scripts\easy_install pyramid
+ c:\> %VENV%\Scripts\easy_install pyramid
-Install SQLite3 and Its Development Packages
+Install SQLite3 and its development packages
--------------------------------------------
If you used a package manager to install your Python or if you compiled
@@ -87,7 +104,7 @@ the Debian system and apt-get, the command would be the following:
$ sudo apt-get install libsqlite3-dev
-Change Directory to Your Virtual Python Environment
+Change directory to your virtual Python environment
---------------------------------------------------
Change directory to the ``pyramidtut`` directory.
@@ -108,7 +125,7 @@ On Windows
.. _sql_making_a_project:
-Making a Project
+Making a project
================
Your next step is to create a project. For this tutorial we will use
@@ -117,17 +134,16 @@ that uses :term:`SQLAlchemy` and :term:`URL dispatch`.
:app:`Pyramid` supplies a variety of scaffolds to generate sample
projects. We will use `pcreate`—a script that comes with Pyramid to
-quickly and easily generate scaffolds usually with a single command—to
+quickly and easily generate scaffolds, usually with a single command—to
create the scaffold for our project.
-By passing in `alchemy` into the `pcreate` command, the script creates
+By passing `alchemy` into the `pcreate` command, the script creates
the files needed to use SQLAlchemy. By passing in our application name
`tutorial`, the script inserts that application name into all the
required files. For example, `pcreate` creates the
``initialize_tutorial_db`` in the ``pyramidtut/bin`` directory.
-The below instructions assume your current working directory is the
-"virtualenv" named "pyramidtut".
+The below instructions assume your current working directory is "pyramidtut".
On UNIX
-------
@@ -141,7 +157,7 @@ On Windows
.. code-block:: text
- c:\pyramidtut> %VENV%\pcreate -s alchemy tutorial
+ c:\pyramidtut> %VENV%\Scripts\pcreate -s alchemy tutorial
.. note:: If you are using Windows, the ``alchemy``
scaffold may not deal gracefully with installation into a
@@ -151,7 +167,7 @@ On Windows
.. _installing_project_in_dev_mode:
-Installing the Project in Development Mode
+Installing the project in development mode
==========================================
In order to do development on the project easily, you must "register"
@@ -184,8 +200,8 @@ the following::
.. _sql_running_tests:
-Running the Tests
-=================
+Run the tests
+=============
After you've installed the project in development mode, you may run
the tests for the project.
@@ -212,8 +228,8 @@ For a successful test run, you should see output that ends like this::
OK
-Exposing Test Coverage Information
-==================================
+Expose test coverage information
+================================
You can run the ``nosetests`` command to see test coverage
information. This runs the tests in the same way that ``setup.py
@@ -258,33 +274,31 @@ On Windows
If successful, you will see output something like this::
- .
- Name Stmts Miss Cover Missing
- ------------------------------------------------
- tutorial 11 7 36% 9-15
- tutorial.models 17 0 100%
- tutorial.scripts 0 0 100%
- tutorial.tests 24 0 100%
- tutorial.views 6 0 100%
- ------------------------------------------------
- TOTAL 58 7 88%
- ----------------------------------------------------------------------
- Ran 1 test in 0.459s
+ .
+ Name Stmts Miss Cover Missing
+ ---------------------------------------------------
+ tutorial.py 13 9 31% 13-21
+ tutorial/models.py 12 0 100%
+ tutorial/scripts.py 0 0 100%
+ tutorial/views.py 11 0 100%
+ ---------------------------------------------------
+ TOTAL 36 9 75%
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.643s
- OK
+ OK
Looks like our package doesn't quite have 100% test coverage.
-
.. _initialize_db_wiki2:
-Initializing the Database
+Initializing the database
=========================
We need to use the ``initialize_tutorial_db`` :term:`console
script` to initialize our database.
-Type the following command, make sure you are still in the ``tutorial``
+Type the following command, making sure you are still in the ``tutorial``
directory (the directory with a ``development.ini`` in it):
On UNIX
@@ -303,28 +317,30 @@ On Windows
The output to your console should be something like this::
- 2011-11-26 14:42:25,012 INFO [sqlalchemy.engine.base.Engine][MainThread]
- PRAGMA table_info("models")
- 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
- 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread]
- CREATE TABLE models (
- id INTEGER NOT NULL,
- name VARCHAR(255),
- value INTEGER,
- PRIMARY KEY (id),
- UNIQUE (name)
- )
- 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
- 2011-11-26 14:42:25,135 INFO [sqlalchemy.engine.base.Engine][MainThread]
- COMMIT
- 2011-11-26 14:42:25,137 INFO [sqlalchemy.engine.base.Engine][MainThread]
- BEGIN (implicit)
- 2011-11-26 14:42:25,138 INFO [sqlalchemy.engine.base.Engine][MainThread]
- INSERT INTO models (name, value) VALUES (?, ?)
- 2011-11-26 14:42:25,139 INFO [sqlalchemy.engine.base.Engine][MainThread]
- (u'one', 1)
- 2011-11-26 14:42:25,140 INFO [sqlalchemy.engine.base.Engine][MainThread]
- COMMIT
+ 2015-05-23 16:49:49,609 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
+ 2015-05-23 16:49:49,609 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] ()
+ 2015-05-23 16:49:49,610 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
+ 2015-05-23 16:49:49,610 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] ()
+ 2015-05-23 16:49:49,610 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("models")
+ 2015-05-23 16:49:49,610 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
+ 2015-05-23 16:49:49,612 INFO [sqlalchemy.engine.base.Engine:1097][MainThread]
+ CREATE TABLE models (
+ id INTEGER NOT NULL,
+ name TEXT,
+ value INTEGER,
+ PRIMARY KEY (id)
+ )
+
+
+ 2015-05-23 16:49:49,612 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
+ 2015-05-23 16:49:49,613 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
+ 2015-05-23 16:49:49,613 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] CREATE UNIQUE INDEX my_index ON models (name)
+ 2015-05-23 16:49:49,613 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
+ 2015-05-23 16:49:49,614 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
+ 2015-05-23 16:49:49,616 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit)
+ 2015-05-23 16:49:49,617 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO models (name, value) VALUES (?, ?)
+ 2015-05-23 16:49:49,617 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('one', 1)
+ 2015-05-23 16:49:49,618 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
Success! You should now have a ``tutorial.sqlite`` file in your current working
directory. This will be a SQLite database with a single table defined in it
@@ -332,8 +348,8 @@ directory. This will be a SQLite database with a single table defined in it
.. _wiki2-start-the-application:
-Starting the Application
-========================
+Start the application
+=====================
Start the application.
@@ -351,6 +367,11 @@ On Windows
c:\pyramidtut\tutorial> %VENV%\Scripts\pserve development.ini --reload
+.. note::
+
+ Your OS firewall, if any, may pop up a dialog asking for authorization
+ to allow python to accept incoming network connections.
+
If successful, you will see something like this on your console::
Starting subprocess with file monitor
@@ -359,31 +380,34 @@ If successful, you will see something like this on your console::
This means the server is ready to accept requests.
-At this point, when you visit ``http://localhost:6543/`` in your web browser,
-you will see the generated application's default page.
+Visit the application in a browser
+==================================
+
+In a browser, visit `http://localhost:6543/ <http://localhost:6543>`_. You
+will see the generated application's default page.
One thing you'll notice is the "debug toolbar" icon on right hand side of the
page. You can read more about the purpose of the icon at
:ref:`debug_toolbar`. It allows you to get information about your
application while you develop.
-Decisions the ``alchemy`` Scaffold Has Made For You
+Decisions the ``alchemy`` scaffold has made for you
=================================================================
-Creating a project using the ``alchemy`` scaffold makes
-the following assumptions:
+Creating a project using the ``alchemy`` scaffold makes the following
+assumptions:
- you are willing to use :term:`SQLAlchemy` as a database access tool
-- you are willing to use :term:`url dispatch` to map URLs to code.
+- you are willing to use :term:`URL dispatch` to map URLs to code
- you want to use ``ZopeTransactionExtension`` and ``pyramid_tm`` to scope
sessions to requests
.. note::
- :app:`Pyramid` supports any persistent storage mechanism (e.g. object
- database or filesystem files, etc). It also supports an additional
+ :app:`Pyramid` supports any persistent storage mechanism (e.g., object
+ database or filesystem files). It also supports an additional
mechanism to map URLs to code (:term:`traversal`). However, for the
- purposes of this tutorial, we'll only be using url dispatch and
+ purposes of this tutorial, we'll only be using URL dispatch and
SQLAlchemy.
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/favicon.ico b/docs/tutorials/wiki2/src/authorization/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki2/src/authorization/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
index ca28b9fa5..ed355434d 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
@@ -1,62 +1,72 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.name} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p tal:condition="logged_in" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ <p>
+ Editing <strong><span tal:replace="page.name">Page Name Goes
+ Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <form action="${save_url}" method="post">
+ <div class="form-group">
+ <textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Editing <b><span tal:replace="page.name">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- <div id="bottom">
- <div class="bottom">
- <form action="${save_url}" method="post">
- <textarea name="body" tal:content="page.data" rows="10"
- cols="60"/><br/>
- <input type="submit" name="form.submitted" value="Save"/>
- </form>
- </div>
- </div>
- </div>
- <div id="footer">
- <div class="footer"
- >&copy; Copyright 2008-2011, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
index 64e592ea9..4a938e9bb 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
@@ -1,58 +1,74 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>Login - Pyramid tutorial wiki (based on TurboGears
- 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Login - Pyramid tutorial wiki (based on
+ TurboGears 20-Minute Wiki)</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p>
+ <strong>
+ Login
+ </strong><br>
+ <span tal:replace="message"></span>
+ </p>
+ <form action="${url}" method="post">
+ <input type="hidden" name="came_from" value="${came_from}">
+ <div class="form-group">
+ <label for="login">Username</label>
+ <input type="text" name="login" value="${login}">
+ </div>
+ <div class="form-group">
+ <label for="password">Password</label>
+ <input type="password" name="password" value="${password}">
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Log In" class="btn btn-default">Log In</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- <b>Login</b><br/>
- <span tal:replace="message"/>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <form action="${url}" method="post">
- <input type="hidden" name="came_from" value="${came_from}"/>
- <input type="text" name="login" value="${login}"/><br/>
- <input type="password" name="password"
- value="${password}"/><br/>
- <input type="submit" name="form.submitted" value="Log In"/>
- </form>
</div>
</div>
- </div>
- <div id="footer">
- <div class="footer"
- >&copy; Copyright 2008-2011, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
index cf3da2073..c9b0cec21 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
@@ -1,76 +1,66 @@
-<!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('tutorial:static/favicon.ico')}" />
- <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" />
- <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top">
- <div class="top align-center">
- <div><img src="${request.static_url('tutorial: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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Alchemy Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
- <div id="footer">
- <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
index 5a69818c1..02cb8e73b 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
@@ -1,65 +1,72 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.name} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p tal:condition="logged_in" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ <p>
+ Viewing <strong><span tal:replace="page.name">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Viewing <b><span tal:replace="page.name">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
- </div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div tal:replace="structure content">
- Page text goes here.
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <p>
- <a tal:attributes="href edit_url" href="">
- Edit this page
- </a>
- </p>
</div>
</div>
- </div>
- <div id="footer">
- <div class="footer"
- >&copy; Copyright 2008-2011, Agendaless Consulting.</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
index 0cdd4bbc3..11ddccadb 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
@@ -2,6 +2,7 @@ from sqlalchemy import (
Column,
Integer,
Text,
+ Index,
)
from sqlalchemy.ext.declarative import declarative_base
@@ -22,3 +23,5 @@ class MyModel(Base):
id = Column(Integer, primary_key=True)
name = Column(Text, unique=True)
value = Column(Integer)
+
+Index('my_index', MyModel.name, unique=True, mysql_length=255)
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/favicon.ico b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
index ca4e0af26..c9b0cec21 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet" href="${request.static_url('tutorial: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('tutorial: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('tutorial: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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Alchemy Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/favicon.ico b/docs/tutorials/wiki2/src/models/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/models/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki2/src/models/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/models/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki2/src/models/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/theme.css b/docs/tutorials/wiki2/src/models/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
index ca4e0af26..c9b0cec21 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet" href="${request.static_url('tutorial: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('tutorial: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('tutorial: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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Alchemy Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/favicon.ico b/docs/tutorials/wiki2/src/tests/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/tests/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/tests/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/tests/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki2/src/tests/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/tests/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki2/src/tests/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki2/src/tests/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/theme.css b/docs/tutorials/wiki2/src/tests/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css
new file mode 100644
index 000000000..2f924bcc5
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/tests/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt
index 2004273fe..50e55c850 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt
@@ -1,58 +1,74 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.name} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p>
+ Editing <strong><span tal:replace="page.name">Page Name Goes
+ Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <p class="pull-right">
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </p>
+ <form action="${save_url}" method="post">
+ <div class="form-group">
+ <textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Editing <b><span tal:replace="page.name">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- <div id="bottom">
- <div class="bottom">
- <form action="${save_url}" method="post">
- <textarea name="body" tal:content="page.data" rows="10"
- cols="60"/><br/>
- <input type="submit" name="form.submitted" value="Save"/>
- </form>
- </div>
- </div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
index 6c1ca924a..c9b0cec21 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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('tutorial:static/favicon.ico')}" />
- <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" />
- <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top">
- <div class="top align-center">
- <div><img src="${request.static_url('tutorial: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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Alchemy Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt
index 19c50fb36..4e5772de0 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt
@@ -1,61 +1,74 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.name} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ <p>
+ Viewing <strong><span tal:replace="page.name">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <p class="pull-right">
+ <span tal:condition="logged_in">
+ <a href="${request.application_url}/logout">Logout</a>
+ </span>
+ </p>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Viewing <b><span tal:replace="page.name">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right">
- <span tal:condition="logged_in">
- <a href="${request.application_url}/logout">Logout</a>
- </span>
- </div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div tal:replace="structure content">
- Page text goes here.
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <p>
- <a tal:attributes="href edit_url" href="">
- Edit this page
- </a>
- </p>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/favicon.ico b/docs/tutorials/wiki2/src/views/tutorial/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png b/docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png b/docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/ie6.css b/docs/tutorials/wiki2/src/views/tutorial/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* 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/tutorials/wiki2/src/views/tutorial/static/middlebg.png b/docs/tutorials/wiki2/src/views/tutorial/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-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/tutorials/wiki2/src/views/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png
index 347e05549..4ab837be9 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/theme.css b/docs/tutorials/wiki2/src/views/tutorial/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/views/tutorial/static/theme.min.css
new file mode 100644
index 000000000..0d25de5b6
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/theme.min.css
@@ -0,0 +1 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif b/docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
index 5f962bbf5..c0c1b6c20 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
@@ -1,54 +1,69 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.name} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <p>
+ Editing <strong><span tal:replace="page.name">Page Name Goes
+ Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ <form action="${save_url}" method="post">
+ <div class="form-group">
+ <textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
+ </div>
+ <div class="form-group">
+ <button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
+ </div>
+ </form>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Editing <b><span tal:replace="page.name">Page Name Goes
- Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <form action="${save_url}" method="post">
- <textarea name="body" tal:content="page.data" rows="10"
- cols="60"/><br/>
- <input type="submit" name="form.submitted" value="Save"/>
- </form>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
index ca4e0af26..c9b0cec21 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
@@ -1,73 +1,66 @@
-<!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('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet" href="${request.static_url('tutorial: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('tutorial: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('tutorial: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/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>Alchemy Scaffold for The Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">${project}</span>, an&nbsp;application generated&nbsp;by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p>
+ </div>
+ </div>
</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/current/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/latest/">Docs</a></li>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
</ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
index 78c0d2d4c..0f564b16c 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
@@ -1,57 +1,69 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>${page.name} - Pyramid tutorial wiki (based on
+<!DOCTYPE html>
+<html lang="${request.locale_name}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
+
+ <title>${page.name} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('tutorial:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('tutorial:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top-small">
- <div class="top-small align-center">
- <div>
- <img width="220" height="50" alt="pyramid"
- src="${request.static_url('tutorial:static/pyramid-small.png')}" />
+
+ <!-- Bootstrap core CSS -->
+ <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ <div class="content">
+ <div tal:replace="structure content">
+ Page text goes here.
+ </div>
+ <p>
+ <a tal:attributes="href edit_url" href="">
+ Edit this page
+ </a>
+ </p>
+ <p>
+ Viewing <strong><span tal:replace="page.name">
+ Page Name Goes Here</span></strong>
+ </p>
+ <p>You can return to the
+ <a href="${request.application_url}">FrontPage</a>.
+ </p>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-right">
- <div id="left" class="app-welcome align-left">
- Viewing <b><span tal:replace="page.name">Page Name
- Goes Here</span></b><br/>
- You can return to the
- <a href="${request.application_url}">FrontPage</a>.<br/>
- </div>
- <div id="right" class="app-welcome align-right"></div>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div tal:replace="structure content">
- Page text goes here.
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
</div>
- <p>
- <a tal:attributes="href edit_url" href="">
- Edit this page
- </a>
- </p>
</div>
</div>
- </div>
-</body>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js"></script>
+ </body>
</html>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views.py b/docs/tutorials/wiki2/src/views/tutorial/views.py
index b41d4ab40..a3707dab5 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/views.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/views.py
@@ -6,6 +6,7 @@ from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
)
+
from pyramid.view import view_config
from .models import (
diff --git a/docs/tutorials/wiki2/tests.rst b/docs/tutorials/wiki2/tests.rst
index 9aca0c5b7..9db95334a 100644
--- a/docs/tutorials/wiki2/tests.rst
+++ b/docs/tutorials/wiki2/tests.rst
@@ -2,29 +2,25 @@
Adding Tests
============
-We will now add tests for the models and the views and a few functional
-tests in the ``tests.py``. Tests ensure that an application works, and
-that it continues to work after changes are made in the future.
+We will now add tests for the models and the views and a few functional tests
+in ``tests.py``. Tests ensure that an application works, and that it
+continues to work when changes are made in the future.
+Test the models
+===============
+To test the model class ``Page`` we'll add a new ``PageModelTests`` class to
+our ``tests.py`` file that was generated as part of the ``alchemy`` scaffold.
-Testing the Models
-==================
+Test the views
+==============
-To test the model class ``Page`` we'll add a new ``PageModelTests``
-class to our ``tests.py`` file that was generated as part of the
-``alchemy`` scaffold.
-
-Testing the Views
-=================
-
-We'll modify our ``tests.py`` file, adding tests for each view
-function we added above. As a result, we'll *delete* the
-``ViewTests`` class that the ``alchemy`` scaffold provided, and add
-four other test classes: ``ViewWikiTests``, ``ViewPageTests``,
-``AddPageTests``, and ``EditPageTests``. These test the
-``view_wiki``, ``view_page``, ``add_page``, and ``edit_page`` views
-respectively.
+We'll modify our ``tests.py`` file, adding tests for each view function we
+added previously. As a result, we'll *delete* the ``ViewTests`` class that
+the ``alchemy`` scaffold provided, and add four other test classes:
+``ViewWikiTests``, ``ViewPageTests``, ``AddPageTests``, and ``EditPageTests``.
+These test the ``view_wiki``, ``view_page``, ``add_page``, and ``edit_page``
+views.
Functional tests
================
@@ -34,16 +30,17 @@ tested in the unit tests, like logging in, logging out, checking that
the ``viewer`` user cannot add or edit pages, but the ``editor`` user
can, and so on.
-Viewing the results of all our edits to ``tests.py``
-====================================================
+View the results of all our edits to ``tests.py``
+=================================================
-Once we're done with the ``tests.py`` module, it will look a lot like:
+Open the ``tutorial/tests.py`` module, and edit it such that it appears as
+follows:
.. literalinclude:: src/tests/tutorial/tests.py
:linenos:
:language: python
-Running the Tests
+Running the tests
=================
We can run these tests by using ``setup.py test`` in the same way we did in
@@ -57,7 +54,7 @@ Change the ``requires`` list in ``setup.py`` to include ``WebTest``.
:lines: 11-22
:emphasize-lines: 11
-After we've added a dependency on WebTest in ``setup.py``, we need to rerun
+After we've added a dependency on WebTest in ``setup.py``, we need to run
``setup.py develop`` to get WebTest installed into our virtualenv. Assuming
our shell's current working directory is the "tutorial" distribution
directory:
@@ -89,7 +86,7 @@ On Windows:
c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py test -q
-The expected result ends something like:
+The expected result should look like the following:
.. code-block:: text
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index a522880c4..34fc49c00 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -848,6 +848,22 @@ class ViewsConfiguratorMixin(object):
think about preserving function attributes such as ``__name__`` and
``__module__`` within decorator logic).
+ All view callables in the decorator chain must return a response
+ object implementing :class:`pyramid.interfaces.IResponse` or raise
+ an exception:
+
+ .. code-block:: python
+
+ def log_timer(wrapped):
+ def wrapper(context, request):
+ start = time.time()
+ response = wrapped(context, request)
+ duration = time.time() - start
+ response.headers['X-View-Time'] = '%.3f' % (duration,)
+ log.info('view took %.3f seconds', duration)
+ return response
+ return wrapper
+
.. versionchanged:: 1.4a4
Passing an iterable.
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index 465769834..8bf9a0a72 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -160,6 +160,13 @@ class HTTPException(Response, Exception):
# title = 'OK'
# explanation = 'why this happens'
# body_template_obj = Template('response template')
+ #
+ # This class itself uses the error code "520" with the error message/title
+ # of "Unknown Error". This is not an RFC standard, however it is
+ # implemented in practice. Sub-classes should be overriding the default
+ # values and 520 should not be seen in the wild from Pyramid applications.
+ # Due to changes in WebOb, a code of "None" is not valid, and WebOb due to
+ # more strict error checking rejects it now.
# differences from webob.exc.WSGIHTTPException:
#
@@ -178,8 +185,8 @@ class HTTPException(Response, Exception):
#
# - documentation improvements (Pyramid-specific docstrings where necessary)
#
- code = None
- title = None
+ code = 520
+ title = 'Unknown Error'
explanation = ''
body_template_obj = Template('''\
${explanation}${br}${br}
@@ -562,10 +569,7 @@ class HTTPClientError(HTTPError):
a bug. A server-side traceback is not warranted. Unless specialized,
this is a '400 Bad Request'
"""
- code = 400
- title = 'Bad Request'
- explanation = ('The server could not comply with the request since '
- 'it is either malformed or otherwise incorrect.')
+ pass
class HTTPBadRequest(HTTPClientError):
"""
@@ -576,7 +580,10 @@ class HTTPBadRequest(HTTPClientError):
code: 400, title: Bad Request
"""
- pass
+ code = 400
+ title = 'Bad Request'
+ explanation = ('The server could not comply with the request since '
+ 'it is either malformed or otherwise incorrect.')
class HTTPUnauthorized(HTTPClientError):
"""
@@ -988,15 +995,15 @@ class HTTPServerError(HTTPError):
This is an error condition in which the server is presumed to be
in-error. Unless specialized, this is a '500 Internal Server Error'.
"""
+ pass
+
+class HTTPInternalServerError(HTTPServerError):
code = 500
title = 'Internal Server Error'
explanation = (
'The server has either erred or is incapable of performing '
'the requested operation.')
-class HTTPInternalServerError(HTTPServerError):
- pass
-
class HTTPNotImplemented(HTTPServerError):
"""
subclass of :class:`~HTTPServerError`
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 7b9a850d1..90534593c 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -1205,7 +1205,7 @@ class ICacheBuster(Interface):
argument is a dict of keywords that are to be passed eventually to
:meth:`~pyramid.request.Request.route_url` for URL generation. The
return value should be a two-tuple of ``(subpath, kw)`` which are
- versions of the same arguments modified to include the cachebust token
+ versions of the same arguments modified to include the cache bust token
in the generated URL.
"""
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 42296bad1..456b16c82 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -308,7 +308,7 @@ class JSON(object):
json_renderer_factory = JSON() # bw compat
-JSONP_VALID_CALLBACK = re.compile(r"^[a-zA-Z_$][0-9a-zA-Z_$]+$")
+JSONP_VALID_CALLBACK = re.compile(r"^[$a-z_][$0-9a-z_\.\[\]]+[^.]$", re.I)
class JSONP(JSON):
""" `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper
@@ -396,7 +396,7 @@ class JSONP(JSON):
raise HTTPBadRequest('Invalid JSONP callback function name.')
ct = 'application/javascript'
- body = '%s(%s);' % (callback, val)
+ body = '/**/{0}({1});'.format(callback, val)
response = request.response
if response.content_type == response.default_content_type:
response.content_type = ct
diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py
index c993ce5f9..4e811a42b 100644
--- a/pyramid/scaffolds/__init__.py
+++ b/pyramid/scaffolds/__init__.py
@@ -18,10 +18,6 @@ class PyramidTemplate(Template):
misnamings (such as naming a package "site" or naming a package
logger "root".
"""
- if vars['package'] == 'site':
- raise ValueError('Sorry, you may not name your package "site". '
- 'The package name "site" has a special meaning in '
- 'Python. Please name it anything except "site".')
vars['random_string'] = native_(binascii.hexlify(os.urandom(20)))
package_logger = vars['package']
if package_logger == 'root':
diff --git a/pyramid/scaffolds/alchemy/+package+/static/theme.css b/pyramid/scaffolds/alchemy/+package+/static/theme.css
index be50ad420..0f4b1a4d4 100644
--- a/pyramid/scaffolds/alchemy/+package+/static/theme.css
+++ b/pyramid/scaffolds/alchemy/+package+/static/theme.css
@@ -72,10 +72,12 @@ p {
color: #f2b7bd;
font-weight: 400;
}
-.starter-template .links ul li a {
- color: #ffffff;
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
}
-.starter-template .links ul li a:hover {
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
text-decoration: underline;
}
.starter-template .links ul li .icon-muted {
diff --git a/pyramid/scaffolds/alchemy/+package+/static/theme.min.css b/pyramid/scaffolds/alchemy/+package+/static/theme.min.css
index 2f924bcc5..0d25de5b6 100644
--- a/pyramid/scaffolds/alchemy/+package+/static/theme.min.css
+++ b/pyramid/scaffolds/alchemy/+package+/static/theme.min.css
@@ -1 +1 @@
-@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}}
diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl
index 448803c8f..07811f7c3 100644
--- a/pyramid/scaffolds/alchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
+host = 127.0.0.1
port = 6543
###
diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl
index c2a28e178..ae9460b11 100644
--- a/pyramid/scaffolds/starter/development.ini_tmpl
+++ b/pyramid/scaffolds/starter/development.ini_tmpl
@@ -24,7 +24,7 @@ pyramid.includes =
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
+host = 127.0.0.1
port = 6543
###
diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl
index 199ddfab4..a44b61686 100644
--- a/pyramid/scaffolds/zodb/development.ini_tmpl
+++ b/pyramid/scaffolds/zodb/development.ini_tmpl
@@ -29,7 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
+host = 127.0.0.1
port = 6543
###
diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py
index f6376f575..1e8074fc5 100644
--- a/pyramid/scripts/pcreate.py
+++ b/pyramid/scripts/pcreate.py
@@ -8,12 +8,17 @@ import os.path
import pkg_resources
import re
import sys
+from pyramid.compat import input_
_bad_chars_re = re.compile('[^a-zA-Z0-9_]')
def main(argv=sys.argv, quiet=False):
command = PCreateCommand(argv, quiet)
- return command.run()
+ try:
+ return command.run()
+ except KeyboardInterrupt: # pragma: no cover
+ return 1
+
class PCreateCommand(object):
verbosity = 1 # required
@@ -52,6 +57,13 @@ class PCreateCommand(object):
dest='interactive',
action='store_true',
help='When a file would be overwritten, interrogate')
+ parser.add_option('--ignore-conflicting-name',
+ dest='force_bad_name',
+ action='store_true',
+ default=False,
+ help='Do create a project even if the chosen name '
+ 'is the name of an already existing / importable '
+ 'package.')
pyramid_dist = pkg_resources.get_distribution("pyramid")
@@ -69,25 +81,19 @@ class PCreateCommand(object):
self.out('')
self.show_scaffolds()
return 2
- if not self.options.scaffold_name:
- self.out('You must provide at least one scaffold name: -s <scaffold name>')
- self.out('')
- self.show_scaffolds()
- return 2
- if not self.args:
- self.out('You must provide a project name')
- return 2
- available = [x.name for x in self.scaffolds]
- diff = set(self.options.scaffold_name).difference(available)
- if diff:
- self.out('Unavailable scaffolds: %s' % list(diff))
+
+ if not self.validate_input():
return 2
+
return self.render_scaffolds()
- def render_scaffolds(self):
- options = self.options
- args = self.args
- output_dir = os.path.abspath(os.path.normpath(args[0]))
+ @property
+ def output_path(self):
+ return os.path.abspath(os.path.normpath(self.args[0]))
+
+ @property
+ def project_vars(self):
+ output_dir = self.output_path
project_name = os.path.basename(os.path.split(output_dir)[1])
pkg_name = _bad_chars_re.sub(
'', project_name.lower().replace('-', '_'))
@@ -111,17 +117,22 @@ class PCreateCommand(object):
else:
pyramid_docs_branch = 'latest'
- vars = {
+ return {
'project': project_name,
'package': pkg_name,
'egg': egg_name,
'pyramid_version': pyramid_version,
'pyramid_docs_branch': pyramid_docs_branch,
- }
- for scaffold_name in options.scaffold_name:
+ }
+
+
+ def render_scaffolds(self):
+ props = self.project_vars
+ output_dir = self.output_path
+ for scaffold_name in self.options.scaffold_name:
for scaffold in self.scaffolds:
if scaffold.name == scaffold_name:
- scaffold.run(self, output_dir, vars)
+ scaffold.run(self, output_dir, props)
return 0
def show_scaffolds(self):
@@ -154,5 +165,48 @@ class PCreateCommand(object):
if not self.quiet:
print(msg)
+ def validate_input(self):
+ if not self.options.scaffold_name:
+ self.out('You must provide at least one scaffold name: -s <scaffold name>')
+ self.out('')
+ self.show_scaffolds()
+ return False
+ if not self.args:
+ self.out('You must provide a project name')
+ return False
+ available = [x.name for x in self.scaffolds]
+ diff = set(self.options.scaffold_name).difference(available)
+ if diff:
+ self.out('Unavailable scaffolds: %s' % ", ".join(sorted(diff)))
+ return False
+
+ pkg_name = self.project_vars['package']
+
+ if pkg_name == 'site' and not self.options.force_bad_name:
+ self.out('The package name "site" has a special meaning in '
+ 'Python. Are you sure you want to use it as your '
+ 'project\'s name?')
+ return self.confirm_bad_name('Really use "{0}"?: '.format(pkg_name))
+
+ # check if pkg_name can be imported (i.e. already exists in current
+ # $PYTHON_PATH, if so - let the user confirm
+ pkg_exists = True
+ try:
+ __import__(pkg_name, globals(), locals(), [], 0) # use absolute imports
+ except ImportError as error:
+ pkg_exists = False
+ if not pkg_exists:
+ return True
+
+ if self.options.force_bad_name:
+ return True
+ self.out('A package named "{0}" already exists, are you sure you want '
+ 'to use it as your project\'s name?'.format(pkg_name))
+ return self.confirm_bad_name('Really use "{0}"?: '.format(pkg_name))
+
+ def confirm_bad_name(self, prompt): # pragma: no cover
+ answer = input_('{0} [y|N]: '.format(prompt))
+ return answer.strip().lower() == 'y'
+
if __name__ == '__main__': # pragma: no cover
sys.exit(main() or 0)
diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py
index e97bdcd48..094a811a4 100644
--- a/pyramid/scripts/pserve.py
+++ b/pyramid/scripts/pserve.py
@@ -200,6 +200,7 @@ class PServeCommand(object):
def run(self): # pragma: no cover
if self.options.stop_daemon:
+ self._warn_daemon_deprecated()
return self.stop_daemon()
if not hasattr(self.options, 'set_user'):
@@ -239,9 +240,11 @@ class PServeCommand(object):
return 2
if cmd == 'status' or self.options.show_status:
+ self._warn_daemon_deprecated()
return self.show_status()
if cmd == 'restart' or cmd == 'stop':
+ self._warn_daemon_deprecated()
result = self.stop_daemon()
if result:
if cmd == 'restart':
@@ -271,6 +274,10 @@ class PServeCommand(object):
server_spec = app_spec
base = os.getcwd()
+ # warn before setting a default
+ if self.options.pid_file:
+ self._warn_daemon_deprecated()
+
if getattr(self.options, 'daemon', False):
if not self.options.pid_file:
self.options.pid_file = 'pyramid.pid'
@@ -296,6 +303,7 @@ class PServeCommand(object):
writeable_pid_file.close()
if getattr(self.options, 'daemon', False):
+ self._warn_daemon_deprecated()
try:
self.daemonize()
except DaemonizeException as ex:
@@ -610,6 +618,15 @@ class PServeCommand(object):
if uid:
os.setuid(uid)
+ def _warn_daemon_deprecated(self):
+ self.out('''\
+The daemon options have been deprecated in Pyramid 1.6. They will be removed
+in a future release per Pyramid's deprecation policy. Please consider using
+a real process manager for your processes like Systemd, Circus, or Supervisor.
+
+The following commands are deprecated:
+ [start,stop,restart,status] --daemon, --stop-server, --status, --pid-file
+''')
class LazyWriter(object):
diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py
index 1168ba78a..5913220fc 100644
--- a/pyramid/scripts/pshell.py
+++ b/pyramid/scripts/pshell.py
@@ -3,6 +3,7 @@ import optparse
import os
import sys
import textwrap
+import pkg_resources
from pyramid.compat import configparser
from pyramid.compat import exec_
@@ -17,6 +18,7 @@ def main(argv=sys.argv, quiet=False):
command = PShellCommand(argv, quiet)
return command.run()
+
class PShellCommand(object):
usage = '%prog config_uri'
description = """\
@@ -32,7 +34,8 @@ class PShellCommand(object):
than one Pyramid application within it, the loader will use the
last one.
"""
- bootstrap = (bootstrap,) # for testing
+ bootstrap = (bootstrap,) # for testing
+ pkg_resources = pkg_resources # for testing
parser = optparse.OptionParser(
usage,
@@ -95,7 +98,7 @@ class PShellCommand(object):
env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:]))
# remove the closer from the env
- closer = env.pop('closer')
+ self.closer = env.pop('closer')
# setup help text for default environment
env_help = dict(env)
@@ -122,7 +125,10 @@ class PShellCommand(object):
# remove any objects from default help that were overidden
for k, v in env.items():
if k not in orig_env or env[k] != orig_env[k]:
- env_help[k] = v
+ if getattr(v, '__doc__', False):
+ env_help[k] = v.__doc__.replace("\n", " ")
+ else:
+ env_help[k] = v
# load the pshell section of the ini file
env.update(self.loaded_objects)
@@ -145,7 +151,12 @@ class PShellCommand(object):
help += '\n %-12s %s' % (var, self.object_help[var])
if shell is None:
- shell = self.make_shell()
+ try:
+ shell = self.make_shell()
+ except ValueError as e:
+ self.out(str(e))
+ self.closer()
+ return 1
if self.pystartup and os.path.isfile(self.pystartup):
with open(self.pystartup, 'rb') as fp:
@@ -156,21 +167,35 @@ class PShellCommand(object):
try:
shell(env, help)
finally:
- closer()
+ self.closer()
def make_shell(self):
+ shells = {}
+
+ for ep in self.pkg_resources.iter_entry_points('pyramid.pshell'):
+ name = ep.name
+ shell_module = ep.load()
+ shells[name] = shell_module
+
shell = None
user_shell = self.options.python_shell.lower()
+
if not user_shell:
- shell = self.make_ipython_shell()
- if shell is None:
- shell = self.make_bpython_shell()
+ sorted_shells = sorted(shells.items(), key=lambda x: x[0])
+ for name, factory in sorted_shells:
+ shell = factory()
- elif user_shell == 'ipython':
- shell = self.make_ipython_shell()
+ if shell is not None:
+ break
+ else:
+ factory = shells.get(user_shell)
- elif user_shell == 'bpython':
- shell = self.make_bpython_shell()
+ if factory is not None:
+ shell = factory()
+ else:
+ raise ValueError(
+ 'could not find a shell named "%s"' % user_shell
+ )
if shell is None:
shell = self.make_default_shell()
@@ -185,7 +210,8 @@ class PShellCommand(object):
interact(banner, local=env)
return shell
- def make_bpython_shell(self, BPShell=None):
+ @classmethod
+ def make_bpython_shell(cls, BPShell=None):
if BPShell is None: # pragma: no cover
try:
from bpython import embed
@@ -196,15 +222,8 @@ class PShellCommand(object):
BPShell(locals_=env, banner=help + '\n')
return shell
- def make_ipython_shell(self):
- shell = self.make_ipython_v1_1_shell()
- if shell is None:
- shell = self.make_ipython_v0_11_shell()
- if shell is None:
- shell = self.make_ipython_v0_10_shell()
- return shell
-
- def make_ipython_v1_1_shell(self, IPShellFactory=None):
+ @classmethod
+ def make_ipython_shell(cls, IPShellFactory=None):
if IPShellFactory is None: # pragma: no cover
try:
from IPython.terminal.embed import (
@@ -217,31 +236,6 @@ class PShellCommand(object):
IPShell()
return shell
- def make_ipython_v0_11_shell(self, IPShellFactory=None):
- if IPShellFactory is None: # pragma: no cover
- try:
- from IPython.frontend.terminal.embed import (
- InteractiveShellEmbed)
- IPShellFactory = InteractiveShellEmbed
- except ImportError:
- return None
- def shell(env, help):
- IPShell = IPShellFactory(banner2=help + '\n', user_ns=env)
- IPShell()
- return shell
-
- def make_ipython_v0_10_shell(self, IPShellFactory=None):
- if IPShellFactory is None: # pragma: no cover
- try:
- from IPython.Shell import IPShellEmbed
- IPShellFactory = IPShellEmbed
- except ImportError:
- return None
- def shell(env, help):
- IPShell = IPShellFactory(argv=[], user_ns=env)
- IPShell.set_banner(IPShell.IP.BANNER + '\n' + help + '\n')
- IPShell()
- return shell
if __name__ == '__main__': # pragma: no cover
sys.exit(main() or 0)
diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py
index d0779e080..b94ef30e4 100644
--- a/pyramid/tests/test_httpexceptions.py
+++ b/pyramid/tests/test_httpexceptions.py
@@ -10,13 +10,22 @@ class Test_exception_response(unittest.TestCase):
from pyramid.httpexceptions import exception_response
return exception_response(*arg, **kw)
+ def test_status_400(self):
+ from pyramid.httpexceptions import HTTPBadRequest
+ self.assertTrue(isinstance(self._callFUT(400), HTTPBadRequest))
+
def test_status_404(self):
from pyramid.httpexceptions import HTTPNotFound
- self.assertEqual(self._callFUT(404).__class__, HTTPNotFound)
+ self.assertTrue(isinstance(self._callFUT(404), HTTPNotFound))
+
+ def test_status_500(self):
+ from pyramid.httpexceptions import HTTPInternalServerError
+ self.assertTrue(isinstance(self._callFUT(500),
+ HTTPInternalServerError))
def test_status_201(self):
from pyramid.httpexceptions import HTTPCreated
- self.assertEqual(self._callFUT(201).__class__, HTTPCreated)
+ self.assertTrue(isinstance(self._callFUT(201), HTTPCreated))
def test_extra_kw(self):
resp = self._callFUT(404, headers=[('abc', 'def')])
@@ -111,7 +120,7 @@ class TestHTTPException(unittest.TestCase):
def test_ctor_calls_Response_ctor(self):
exc = self._makeOne('message')
- self.assertEqual(exc.status, 'None None')
+ self.assertEqual(exc.status, '520 Unknown Error')
def test_ctor_extends_headers(self):
exc = self._makeOne(headers=[('X-Foo', 'foo')])
@@ -320,7 +329,7 @@ class Test_HTTPMove(unittest.TestCase):
start_response = DummyStartResponse()
app_iter = exc(environ, start_response)
self.assertEqual(app_iter[0],
- (b'None None\n\nThe resource has been moved to foo; '
+ (b'520 Unknown Error\n\nThe resource has been moved to foo; '
b'you should be redirected automatically.\n\n'))
class TestHTTPForbidden(unittest.TestCase):
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 61a798ad1..2458ea830 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -669,7 +669,17 @@ class TestJSONP(unittest.TestCase):
request = testing.DummyRequest()
request.GET['callback'] = 'callback'
result = renderer({'a':'1'}, {'request':request})
- self.assertEqual(result, 'callback({"a": "1"});')
+ self.assertEqual(result, '/**/callback({"a": "1"});')
+ self.assertEqual(request.response.content_type,
+ 'application/javascript')
+
+ def test_render_to_jsonp_with_dot(self):
+ renderer_factory = self._makeOne()
+ renderer = renderer_factory(None)
+ request = testing.DummyRequest()
+ request.GET['callback'] = 'angular.callbacks._0'
+ result = renderer({'a':'1'}, {'request':request})
+ self.assertEqual(result, '/**/angular.callbacks._0({"a": "1"});')
self.assertEqual(request.response.content_type,
'application/javascript')
diff --git a/pyramid/tests/test_scaffolds/test_init.py b/pyramid/tests/test_scaffolds/test_init.py
index 4988e66ff..f4d1b287a 100644
--- a/pyramid/tests/test_scaffolds/test_init.py
+++ b/pyramid/tests/test_scaffolds/test_init.py
@@ -12,11 +12,6 @@ class TestPyramidTemplate(unittest.TestCase):
self.assertTrue(vars['random_string'])
self.assertEqual(vars['package_logger'], 'one')
- def test_pre_site(self):
- inst = self._makeOne()
- vars = {'package':'site'}
- self.assertRaises(ValueError, inst.pre, 'command', 'output dir', vars)
-
def test_pre_root(self):
inst = self._makeOne()
vars = {'package':'root'}
diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py
index 930b9ed64..2788c0b32 100644
--- a/pyramid/tests/test_scripts/dummy.py
+++ b/pyramid/tests/test_scripts/dummy.py
@@ -40,9 +40,6 @@ class DummyIPShell(object):
IP = Dummy()
IP.BANNER = 'foo'
- def set_banner(self, banner):
- self.banner = banner
-
def __call__(self):
self.called = True
@@ -157,3 +154,23 @@ class DummyBootstrap(object):
'root_factory': self.root_factory,
'closer': self.closer,
}
+
+
+class DummyEntryPoint(object):
+ def __init__(self, name, module):
+ self.name = name
+ self.module = module
+
+ def load(self):
+ return self.module
+
+
+class DummyPkgResources(object):
+ def __init__(self, entry_point_values):
+ self.entry_points = []
+
+ for name, module in entry_point_values.items():
+ self.entry_points.append(DummyEntryPoint(name, module))
+
+ def iter_entry_points(self, name):
+ return self.entry_points
diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py
index 63e5e6368..eaa7c1464 100644
--- a/pyramid/tests/test_scripts/test_pcreate.py
+++ b/pyramid/tests/test_scripts/test_pcreate.py
@@ -1,5 +1,6 @@
import unittest
+
class TestPCreateCommand(unittest.TestCase):
def setUp(self):
from pyramid.compat import NativeIO
@@ -15,7 +16,8 @@ class TestPCreateCommand(unittest.TestCase):
def _makeOne(self, *args, **kw):
effargs = ['pcreate']
effargs.extend(args)
- cmd = self._getTargetClass()(effargs, **kw)
+ tgt_class = kw.pop('target_class', self._getTargetClass())
+ cmd = tgt_class(effargs, **kw)
cmd.out = self.out
return cmd
@@ -220,6 +222,48 @@ class TestPCreateCommand(unittest.TestCase):
'pyramid_version': '0.10.1dev',
'pyramid_docs_branch': 'master'})
+ def test_confirm_override_conflicting_name(self):
+ from pyramid.scripts.pcreate import PCreateCommand
+ class YahInputPCreateCommand(PCreateCommand):
+ def confirm_bad_name(self, pkg_name):
+ return True
+ cmd = self._makeOne('-s', 'dummy', 'Unittest', target_class=YahInputPCreateCommand)
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.10.1dev")
+ result = cmd.run()
+ self.assertEqual(result, 0)
+ self.assertEqual(
+ scaffold.vars,
+ {'project': 'Unittest', 'egg': 'Unittest', 'package': 'unittest',
+ 'pyramid_version': '0.10.1dev',
+ 'pyramid_docs_branch': 'master'})
+
+ def test_force_override_conflicting_name(self):
+ cmd = self._makeOne('-s', 'dummy', 'Unittest', '--ignore-conflicting-name')
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.10.1dev")
+ result = cmd.run()
+ self.assertEqual(result, 0)
+ self.assertEqual(
+ scaffold.vars,
+ {'project': 'Unittest', 'egg': 'Unittest', 'package': 'unittest',
+ 'pyramid_version': '0.10.1dev',
+ 'pyramid_docs_branch': 'master'})
+
+ def test_force_override_site_name(self):
+ from pyramid.scripts.pcreate import PCreateCommand
+ class NayInputPCreateCommand(PCreateCommand):
+ def confirm_bad_name(self, pkg_name):
+ return False
+ cmd = self._makeOne('-s', 'dummy', 'Site', target_class=NayInputPCreateCommand)
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.10.1dev")
+ result = cmd.run()
+ self.assertEqual(result, 2)
+
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py
index 75d4f5bef..2d4c4e1c0 100644
--- a/pyramid/tests/test_scripts/test_pserve.py
+++ b/pyramid/tests/test_scripts/test_pserve.py
@@ -180,14 +180,14 @@ class TestPServeCommand(unittest.TestCase):
inst = self._makeOne('--stop-daemon', '--pid-file=%s' % path)
inst.run()
msg = 'No PID file exists in %s' % path
- self.assertEqual(self.out_.getvalue(), msg)
+ self.assertTrue(msg in self.out_.getvalue())
def test_run_stop_daemon_bad_pid_file(self):
path = __file__
inst = self._makeOne('--stop-daemon', '--pid-file=%s' % path)
inst.run()
msg = 'Not a valid PID file in %s' % path
- self.assertEqual(self.out_.getvalue(), msg)
+ self.assertTrue(msg in self.out_.getvalue())
def test_run_stop_daemon_invalid_pid_in_file(self):
fn = tempfile.mktemp()
@@ -197,7 +197,7 @@ class TestPServeCommand(unittest.TestCase):
inst = self._makeOne('--stop-daemon', '--pid-file=%s' % fn)
inst.run()
msg = 'PID in %s is not valid (deleting)' % fn
- self.assertEqual(self.out_.getvalue(), msg)
+ self.assertTrue(msg in self.out_.getvalue())
def test_get_options_with_command(self):
inst = self._makeOne()
diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py
index dab32fecd..034f2109d 100644
--- a/pyramid/tests/test_scripts/test_pshell.py
+++ b/pyramid/tests/test_scripts/test_pshell.py
@@ -2,6 +2,7 @@ import os
import unittest
from pyramid.tests.test_scripts import dummy
+
class TestPShellCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.pshell import PShellCommand
@@ -10,6 +11,7 @@ class TestPShellCommand(unittest.TestCase):
def _makeOne(self, patch_bootstrap=True, patch_config=True,
patch_args=True, patch_options=True):
cmd = self._getTargetClass()([])
+
if patch_bootstrap:
self.bootstrap = dummy.DummyBootstrap()
cmd.bootstrap = (self.bootstrap,)
@@ -25,11 +27,15 @@ class TestPShellCommand(unittest.TestCase):
self.options.python_shell = ''
self.options.setup = None
cmd.options = self.options
+
# default to None to prevent side-effects from running tests in
# unknown environments
cmd.pystartup = None
return cmd
+ def _makeEntryPoints(self, command, shells):
+ command.pkg_resources = dummy.DummyPkgResources(shells)
+
def test_make_default_shell(self):
command = self._makeOne()
interact = dummy.DummyInteractor()
@@ -49,36 +55,23 @@ class TestPShellCommand(unittest.TestCase):
def test_make_ipython_v1_1_shell(self):
command = self._makeOne()
ipshell_factory = dummy.DummyIPShellFactory()
- shell = command.make_ipython_v1_1_shell(ipshell_factory)
- shell({'foo': 'bar'}, 'a help message')
- self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
- self.assertTrue('a help message' in ipshell_factory.kw['banner2'])
- self.assertTrue(ipshell_factory.shell.called)
-
- def test_make_ipython_v0_11_shell(self):
- command = self._makeOne()
- ipshell_factory = dummy.DummyIPShellFactory()
- shell = command.make_ipython_v0_11_shell(ipshell_factory)
+ shell = command.make_ipython_shell(ipshell_factory)
shell({'foo': 'bar'}, 'a help message')
self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
self.assertTrue('a help message' in ipshell_factory.kw['banner2'])
self.assertTrue(ipshell_factory.shell.called)
- def test_make_ipython_v0_10_shell(self):
- command = self._makeOne()
- ipshell_factory = dummy.DummyIPShellFactory()
- shell = command.make_ipython_v0_10_shell(ipshell_factory)
- shell({'foo': 'bar'}, 'a help message')
- self.assertEqual(ipshell_factory.kw['argv'], [])
- self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
- self.assertTrue('a help message' in ipshell_factory.shell.banner)
- self.assertTrue(ipshell_factory.shell.called)
-
def test_command_loads_default_shell(self):
command = self._makeOne()
shell = dummy.DummyShell()
- command.make_ipython_shell = lambda: None
- command.make_bpython_shell = lambda: None
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: None,
+ 'bpython': lambda: None,
+ }
+ )
+
command.make_default_shell = lambda: shell
command.run()
self.assertTrue(self.config_factory.parser)
@@ -96,82 +89,50 @@ class TestPShellCommand(unittest.TestCase):
def test_command_loads_default_shell_with_unknown_shell(self):
command = self._makeOne()
+ out_calls = []
+
+ def out(msg):
+ out_calls.append(msg)
+
+ command.out = out
+
shell = dummy.DummyShell()
bad_shell = dummy.DummyShell()
- command.make_ipython_shell = lambda: bad_shell
- command.make_bpython_shell = lambda: bad_shell
+
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: bad_shell,
+ 'bpython': lambda: bad_shell,
+ }
+ )
+
command.make_default_shell = lambda: shell
- command.options.python_shell = 'unknow_python_shell'
- command.run()
+ command.options.python_shell = 'unknown_python_shell'
+ result = command.run()
+ self.assertEqual(result, 1)
+ self.assertEqual(
+ out_calls, ['could not find a shell named "unknown_python_shell"']
+ )
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
- self.assertEqual(shell.env, {
- 'app':self.bootstrap.app, 'root':self.bootstrap.root,
- 'registry':self.bootstrap.registry,
- 'request':self.bootstrap.request,
- 'root_factory':self.bootstrap.root_factory,
- })
- self.assertEqual(bad_shell.env, {})
self.assertTrue(self.bootstrap.closer.called)
- self.assertTrue(shell.help)
def test_command_loads_ipython_v1_1(self):
command = self._makeOne()
shell = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: shell
- command.make_ipython_v0_11_shell = lambda: None
- command.make_ipython_v0_10_shell = lambda: None
- command.make_bpython_shell = lambda: None
- command.make_default_shell = lambda: None
- command.options.python_shell = 'ipython'
- command.run()
- self.assertTrue(self.config_factory.parser)
- self.assertEqual(self.config_factory.parser.filename,
- '/foo/bar/myapp.ini')
- self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
- self.assertEqual(shell.env, {
- 'app':self.bootstrap.app, 'root':self.bootstrap.root,
- 'registry':self.bootstrap.registry,
- 'request':self.bootstrap.request,
- 'root_factory':self.bootstrap.root_factory,
- })
- self.assertTrue(self.bootstrap.closer.called)
- self.assertTrue(shell.help)
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: shell,
+ 'bpython': lambda: bad_shell,
+ }
+ )
- def test_command_loads_ipython_v0_11(self):
- command = self._makeOne()
- shell = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: None
- command.make_ipython_v0_11_shell = lambda: shell
- command.make_ipython_v0_10_shell = lambda: None
- command.make_bpython_shell = lambda: None
- command.make_default_shell = lambda: None
command.options.python_shell = 'ipython'
- command.run()
- self.assertTrue(self.config_factory.parser)
- self.assertEqual(self.config_factory.parser.filename,
- '/foo/bar/myapp.ini')
- self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
- self.assertEqual(shell.env, {
- 'app':self.bootstrap.app, 'root':self.bootstrap.root,
- 'registry':self.bootstrap.registry,
- 'request':self.bootstrap.request,
- 'root_factory':self.bootstrap.root_factory,
- })
- self.assertTrue(self.bootstrap.closer.called)
- self.assertTrue(shell.help)
- def test_command_loads_ipython_v0_10(self):
- command = self._makeOne()
- shell = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: None
- command.make_ipython_v0_11_shell = lambda: None
- command.make_ipython_v0_10_shell = lambda: shell
- command.make_bpython_shell = lambda: None
- command.make_default_shell = lambda: None
- command.options.python_shell = 'ipython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
@@ -189,8 +150,15 @@ class TestPShellCommand(unittest.TestCase):
def test_command_loads_bpython_shell(self):
command = self._makeOne()
shell = dummy.DummyBPythonShell()
- command.make_ipython_shell = lambda: None
- command.make_bpython_shell = lambda: shell
+
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: None,
+ 'bpython': lambda: shell,
+ }
+ )
+
command.options.python_shell = 'bpython'
command.run()
self.assertTrue(self.config_factory.parser)
@@ -206,37 +174,36 @@ class TestPShellCommand(unittest.TestCase):
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.banner)
- def test_shell_ipython_ordering(self):
+ def test_shell_entry_points(self):
command = self._makeOne()
- shell1_1 = dummy.DummyShell()
- shell0_11 = dummy.DummyShell()
- shell0_10 = dummy.DummyShell()
- command.make_ipython_v1_1_shell = lambda: shell1_1
- shell = command.make_shell()
- self.assertEqual(shell, shell1_1)
-
- command.make_ipython_v1_1_shell = lambda: None
- command.make_ipython_v0_11_shell = lambda: shell0_11
- shell = command.make_shell()
- self.assertEqual(shell, shell0_11)
+ dshell = dummy.DummyShell()
- command.make_ipython_v0_11_shell = lambda: None
- command.make_ipython_v0_10_shell = lambda: shell0_10
- shell = command.make_shell()
- self.assertEqual(shell, shell0_10)
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: dshell,
+ 'bpython': lambda: dshell,
+ }
+ )
- command.options.python_shell = 'ipython'
- command.make_ipython_v1_1_shell = lambda: shell1_1
+ command.make_default_shell = lambda: None
shell = command.make_shell()
- self.assertEqual(shell, shell1_1)
+ self.assertEqual(shell, dshell)
def test_shell_ordering(self):
command = self._makeOne()
ipshell = dummy.DummyShell()
bpshell = dummy.DummyShell()
dshell = dummy.DummyShell()
- command.make_ipython_shell = lambda: None
- command.make_bpython_shell = lambda: None
+
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: None,
+ 'bpython': lambda: None,
+ }
+ )
+
command.make_default_shell = lambda: dshell
shell = command.make_shell()
@@ -250,8 +217,15 @@ class TestPShellCommand(unittest.TestCase):
shell = command.make_shell()
self.assertEqual(shell, dshell)
- command.make_ipython_shell = lambda: ipshell
- command.make_bpython_shell = lambda: bpshell
+ self._makeEntryPoints(
+ command,
+ {
+ 'ipython': lambda: ipshell,
+ 'bpython': lambda: bpshell,
+ 'python': lambda: dshell,
+ }
+ )
+
command.options.python_shell = 'ipython'
shell = command.make_shell()
self.assertEqual(shell, ipshell)
@@ -289,6 +263,7 @@ class TestPShellCommand(unittest.TestCase):
def setup(env):
env['a'] = 1
env['root'] = 'root override'
+ env['none'] = None
self.config_factory.items = [('setup', setup)]
shell = dummy.DummyShell()
command.run(shell)
@@ -302,6 +277,7 @@ class TestPShellCommand(unittest.TestCase):
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
'a':1,
+ 'none': None,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index b013ffa66..eac6593d9 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -1,3 +1,4 @@
+import base64
import json
import unittest
from pyramid import testing
@@ -277,7 +278,7 @@ class TestBaseCookieSession(SharedCookieSessionTests, unittest.TestCase):
return BaseCookieSessionFactory(serializer, **kw)(request)
def _serialize(self, value):
- return json.dumps(value)
+ return base64.b64encode(json.dumps(value).encode('utf-8'))
def test_reissue_not_triggered(self):
import time
@@ -650,10 +651,16 @@ class Test_check_csrf_token(unittest.TestCase):
class DummySerializer(object):
def dumps(self, value):
- return json.dumps(value).encode('utf-8')
+ return base64.b64encode(json.dumps(value).encode('utf-8'))
def loads(self, value):
- return json.loads(value.decode('utf-8'))
+ try:
+ return json.loads(base64.b64decode(value).decode('utf-8'))
+
+ # base64.b64decode raises a TypeError on py2 instead of a ValueError
+ # and a ValueError is required for the session to handle it properly
+ except TypeError:
+ raise ValueError
class DummySessionFactory(dict):
_dirty = False
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index ff73a93ab..e6b9f9e7e 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -185,6 +185,20 @@ class RenderViewToResponseTests(BaseTest, unittest.TestCase):
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.app_iter, ['anotherview'])
+ def test_call_view_with_request_iface_on_request(self):
+ # See https://github.com/Pylons/pyramid/issues/1643
+ from zope.interface import Interface
+ class IWontBeFound(Interface): pass
+ context = self._makeContext()
+ request = self._makeRequest()
+ request.request_iface = IWontBeFound
+ response = DummyResponse('aview')
+ view = make_view(response)
+ self._registerView(request.registry, view, 'aview')
+ response = self._callFUT(context, request, name='aview')
+ self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.app_iter, ['aview'])
+
class RenderViewToIterableTests(BaseTest, unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.view import render_view_to_iterable
diff --git a/pyramid/util.py b/pyramid/util.py
index 1ae7e6afc..5e43383b7 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -554,7 +554,14 @@ def action_method(wrapped):
info = ActionInfo(*info)
if info is None:
try:
- f = traceback.extract_stack(limit=3)
+ f = traceback.extract_stack(limit=4)
+
+ # Work around a Python 3.5 issue whereby it would insert an
+ # extra stack frame. This should no longer be necessary in
+ # Python 3.5.1
+ last_frame = ActionInfo(*f[-1])
+ if last_frame.function == 'extract_stack': # pragma: no cover
+ f.pop()
info = ActionInfo(*f[-backframes])
except: # pragma: no cover
info = ActionInfo(None, 0, '', '')
diff --git a/pyramid/view.py b/pyramid/view.py
index 005e81148..2867e3d6f 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -49,6 +49,19 @@ def render_view_to_response(context, request, name='', secure=True):
registry = get_current_registry()
context_iface = providedBy(context)
+ # We explicitly pass in the interfaces provided by the request as
+ # request_iface to _call_view; we don't want _call_view to use
+ # request.request_iface, because render_view_to_response and friends are
+ # pretty much limited to finding views that are not views associated with
+ # routes, and the only thing request.request_iface is used for is to find
+ # route-based views. The render_view_to_response API is (and always has
+ # been) a stepchild API reserved for use of those who actually use
+ # traversal. Doing this fixes an infinite recursion bug introduced in
+ # Pyramid 1.6a1, and causes the render_view* APIs to behave as they did in
+ # 1.5 and previous. We should probably provide some sort of different API
+ # that would allow people to find views for routes. See
+ # https://github.com/Pylons/pyramid/issues/1643 for more info.
+ request_iface = providedBy(request)
response = _call_view(
registry,
@@ -57,6 +70,7 @@ def render_view_to_response(context, request, name='', secure=True):
context_iface,
name,
secure=secure,
+ request_iface=request_iface,
)
return response # NB: might be None
diff --git a/rtd.txt b/rtd.txt
index 45be10175..142b6ca35 100644
--- a/rtd.txt
+++ b/rtd.txt
@@ -1,5 +1 @@
-Sphinx >= 1.2.3
-repoze.sphinx.autointerface
-repoze.lru
-pylons_sphinx_latesturl
-pylons-sphinx-themes \ No newline at end of file
+-e .[docs]
diff --git a/setup.py b/setup.py
index ac03da9f5..2a4271138 100644
--- a/setup.py
+++ b/setup.py
@@ -56,10 +56,11 @@ if not PY3:
tests_require.append('zope.component>=3.11.0')
docs_extras = [
- 'Sphinx >= 1.2.3',
+ 'Sphinx >= 1.3.1',
'docutils',
'repoze.sphinx.autointerface',
- 'pylons-sphinx-themes >= 0.3',
+ 'pylons_sphinx_latesturl',
+ 'pylons-sphinx-themes',
]
testing_extras = tests_require + [
@@ -82,6 +83,7 @@ setup(name='pyramid',
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Framework :: Pyramid",
@@ -109,6 +111,9 @@ setup(name='pyramid',
starter=pyramid.scaffolds:StarterProjectTemplate
zodb=pyramid.scaffolds:ZODBProjectTemplate
alchemy=pyramid.scaffolds:AlchemyProjectTemplate
+ [pyramid.pshell]
+ ipython=pyramid.scripts.pshell:PShellCommand.make_ipython_shell
+ bpython=pyramid.scripts.pshell:PShellCommand.make_bpython_shell
[console_scripts]
pcreate = pyramid.scripts.pcreate:main
pserve = pyramid.scripts.pserve:main
diff --git a/tox.ini b/tox.ini
index f3b21561f..20a9ee5b1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
envlist =
- py26,py27,py32,py33,py34,pypy,pypy3,pep8,
+ py26,py27,py32,py33,py34,py35,pypy,pypy3,pep8,
{py2,py3}-docs,
{py2,py3}-cover,coverage,
@@ -13,10 +13,11 @@ basepython =
py32: python3.2
py33: python3.3
py34: python3.4
+ py35: python3.5
pypy: pypy
pypy3: pypy3
py2: python2.7
- py3: python3.4
+ py3: python3.5
commands =
pip install pyramid[testing]
@@ -57,13 +58,13 @@ setenv =
whitelist_externals = make
commands =
pip install pyramid[docs]
- make -C docs html epub BUILDDIR={envdir}
+ make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E"
[testenv:py3-docs]
whitelist_externals = make
commands =
pip install pyramid[docs]
- make -C docs html epub BUILDDIR={envdir}
+ make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E"
[testenv:py26-scaffolds]
basepython = python2.6