summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2016-12-10 00:01:05 -0600
committerMichael Merickel <michael@merickel.org>2016-12-10 00:01:05 -0600
commit1fde5f47b8b6208a25e951c6d3887cc73cc3696e (patch)
tree1975f4d89d0422aaabd09b9bf94ac0da93534f8d
parentff0da73a8922b5e82c676715078c7b9e60a6a1da (diff)
parent2d45def603f038a8533eb9790640982012c0be30 (diff)
downloadpyramid-1fde5f47b8b6208a25e951c6d3887cc73cc3696e.tar.gz
pyramid-1fde5f47b8b6208a25e951c6d3887cc73cc3696e.tar.bz2
pyramid-1fde5f47b8b6208a25e951c6d3887cc73cc3696e.zip
Merge branch 'master' into pserve-watch-files
-rw-r--r--CHANGES.txt12
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--HACKING.txt4
-rw-r--r--RELEASING.txt4
-rw-r--r--docs/api/i18n.rst1
-rw-r--r--docs/conf.py13
-rw-r--r--docs/conventions.rst107
-rw-r--r--docs/index.rst7
-rw-r--r--docs/latexindex.rst2
-rw-r--r--docs/narr/install.rst2
-rw-r--r--docs/narr/introduction.rst2
-rw-r--r--docs/quick_tour.rst4
-rw-r--r--docs/quick_tutorial/requirements.rst6
-rw-r--r--docs/style-guide.rst1258
-rw-r--r--docs/tutorials/wiki/authorization.rst41
-rw-r--r--docs/tutorials/wiki/definingviews.rst1
-rw-r--r--docs/tutorials/wiki/installation.rst6
-rw-r--r--docs/tutorials/wiki/src/authorization/setup.py1
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/security.py17
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/views.py4
-rw-r--r--docs/tutorials/wiki/src/tests/setup.py1
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/security.py17
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/tests.py11
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/views.py4
-rw-r--r--docs/tutorials/wiki2/installation.rst6
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py1
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py12
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py20
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py21
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py67
-rw-r--r--docs/typographical-conventions.rst338
-rw-r--r--docs/whatsnew-1.7.rst2
-rw-r--r--pyramid/asset.py2
-rw-r--r--pyramid/config/predicates.py303
-rw-r--r--pyramid/config/routes.py10
-rw-r--r--pyramid/config/security.py2
-rw-r--r--pyramid/config/tweens.py2
-rw-r--r--pyramid/config/util.py15
-rw-r--r--pyramid/config/views.py6
-rw-r--r--pyramid/httpexceptions.py2
-rw-r--r--pyramid/predicates.py300
-rw-r--r--pyramid/tests/test_config/test_util.py11
-rw-r--r--pyramid/tests/test_config/test_views.py4
-rw-r--r--pyramid/tests/test_httpexceptions.py2
-rw-r--r--pyramid/tests/test_predicates.py (renamed from pyramid/tests/test_config/test_predicates.py)22
-rw-r--r--pyramid/util.py17
-rw-r--r--setup.py21
47 files changed, 2214 insertions, 499 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 0686e1ad1..11eab9f26 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -42,6 +42,9 @@ Backward Incompatibilities
Features
--------
+- Python 3.6 compatibility.
+ https://github.com/Pylons/pyramid/issues/2835
+
- pcreate learned about --package-name to allow you to create a new project in
an existing folder with a different package name than the project name. See
https://github.com/Pylons/pyramid/pull/2783
@@ -120,6 +123,12 @@ Features
``watch_files`` key that can configure ``pserve --reload`` to monitor custom
file paths. See https://github.com/Pylons/pyramid/pull/2827
+- Allow streaming responses to be made from subclasses of
+ ``pyramid.httpexceptions.HTTPException``. Previously the response would
+ be unrolled while testing for a body, making it impossible to stream
+ a response.
+ See https://github.com/Pylons/pyramid/pull/2863
+
Bug Fixes
---------
@@ -155,6 +164,9 @@ Deprecations
Documentation Changes
---------------------
+- Replace Typographical Conventions with an enhanced Style Guide.
+ https://github.com/Pylons/pyramid/pull/2838
+
- Add pyramid_nacl_session to session factories.
See https://github.com/Pylons/pyramid/issues/2791
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 98e243c1f..b4e30e085 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -286,3 +286,5 @@ Contributors
- Keith Yang, 2016/07/22
- Moriyoshi Koizumi, 2016/11/20
+
+- Mikko Ohtamaa, 2016/12/6
diff --git a/HACKING.txt b/HACKING.txt
index f240492e7..bbebb5165 100644
--- a/HACKING.txt
+++ b/HACKING.txt
@@ -118,8 +118,8 @@ In order to add a feature to Pyramid:
- The feature must be documented in both the API and narrative documentation
(in ``docs/``).
-- The feature must work fully on the following CPython versions: 2.7, 3.4,
- and 3.5 on both UNIX and Windows.
+- The feature must work fully on the following CPython versions: 2.7, 3.4, 3.5,
+ and 3.6 on both UNIX and Windows.
- The feature must work on the latest version of PyPy.
diff --git a/RELEASING.txt b/RELEASING.txt
index 4690fbd37..73cf38aa7 100644
--- a/RELEASING.txt
+++ b/RELEASING.txt
@@ -33,8 +33,8 @@ Prepare new release branch
- Run tests on Windows if feasible.
-- Make sure all scaffold tests pass (CPython 2.7, 3.4, and 3.5, and PyPy on
- UNIX; this doesn't work on Windows):
+- Make sure all scaffold tests pass (CPython 2.7, 3.4, 3.5, and 3.6, and PyPy
+ on UNIX; this doesn't work on Windows):
$ ./scaffoldtests.sh
diff --git a/docs/api/i18n.rst b/docs/api/i18n.rst
index 3b9abbc1d..7a61246df 100644
--- a/docs/api/i18n.rst
+++ b/docs/api/i18n.rst
@@ -6,6 +6,7 @@
.. automodule:: pyramid.i18n
.. autoclass:: TranslationString
+ :noindex:
.. autofunction:: TranslationStringFactory
diff --git a/docs/conf.py b/docs/conf.py
index c3a7170fc..12dd27722 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,10 @@ book = os.environ.get('BOOK')
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
- 'repoze.sphinx.autointerface',
- 'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.viewcode',
+ 'repoze.sphinx.autointerface',
'sphinxcontrib.programoutput',
# enable pylons_sphinx_latesturl when this branch is no longer "latest"
# 'pylons_sphinx_latesturl',
@@ -68,6 +69,7 @@ intersphinx_mapping = {
'pylonswebframework': ('http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/', None),
'python': ('https://docs.python.org/3', None),
'pytest': ('http://pytest.org/latest/', None),
+ 'sphinx': ('http://www.sphinx-doc.org/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),
@@ -119,6 +121,9 @@ exclude_patterns = ['_themes/README.rst', ]
# unit titles (such as .. function::).
add_module_names = False
+# Add support for todo items
+todo_include_todos = True
+
# The name of the Pygments (syntax highlighting) style to use.
#pygments_style = book and 'bw' or 'tango'
if book:
@@ -191,10 +196,10 @@ latex_documents = [
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-latex_use_parts = True
+latex_toplevel_sectioning = "section"
# If false, no module index is generated.
-latex_use_modindex = False
+latex_domain_indices = False
## Say, for a moment that you have a twoside document that needs a 3cm
## inner margin to allow for binding and at least two centimetres the
diff --git a/docs/conventions.rst b/docs/conventions.rst
deleted file mode 100644
index de041da04..000000000
--- a/docs/conventions.rst
+++ /dev/null
@@ -1,107 +0,0 @@
-Typographical Conventions
-=========================
-
-Literals, filenames, and function arguments are presented using the
-following style:
-
- ``argument1``
-
-Warnings which represent limitations and need-to-know information
-related to a topic or concept are presented in the following style:
-
- .. warning::
-
- This is a warning.
-
-Notes which represent additional information related to a topic or
-concept are presented in the following style:
-
- .. note::
-
- This is a note.
-
-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
-variables using the following style:
-
- :class:`pyramid.config.Configurator.registry`
-
-References to glossary terms are presented using the following style:
-
- :term:`Pylons`
-
-URLs are presented using the following style:
-
- `Pylons <http://www.pylonsproject.org>`_
-
-References to sections and chapters are presented using the following
-style:
-
- :ref:`traversal_chapter`
-
-Code and configuration file blocks are presented in the following style:
-
- .. code-block:: python
- :linenos:
-
- def foo(abc):
- pass
-
-Example blocks representing UNIX shell commands are prefixed with a ``$``
-character, e.g.:
-
- .. code-block:: bash
-
- $ $VENV/bin/py.test -q
-
-See :term:`venv` for the meaning of ``$VENV``.
-
-Example blocks representing Windows commands are prefixed with a drive letter
-with an optional directory name, e.g.:
-
- .. code-block:: doscon
-
- c:\examples> %VENV%\Scripts\py.test -q
-
-See :term:`venv` for the meaning of ``%VENV%``.
-
-When a command that should be typed on one line is too long to fit on a page,
-the backslash ``\`` is used to indicate that the following printed line should
-be part of the command:
-
- .. code-block:: bash
-
- $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \
- --cov=tutorial -q
-
-A sidebar, which presents a concept tangentially related to content discussed
-on a page, is rendered like so:
-
-.. sidebar:: This is a sidebar
-
- Sidebar information.
-
-When multiple objects are imported from the same package, the following
-convention is used:
-
- .. code-block:: python
-
- from foo import (
- bar,
- baz,
- )
-
-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).
-
-* Looks more neat in cases where a large number of objects get imported from
- that package.
-
-* Adding or removing imported objects from the package is quicker and results
- in simpler diffs.
diff --git a/docs/index.rst b/docs/index.rst
index 02c35866a..a783e8a70 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -213,13 +213,14 @@ Copyright, Trademarks, and Attributions
copyright
-Typographical Conventions
-=========================
+Typographical Conventions and Style Guide
+=========================================
.. toctree::
:maxdepth: 1
- conventions
+ typographical-conventions
+ style-guide
Index and Glossary
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index 05199d313..83a139917 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -15,7 +15,7 @@ Front Matter
:maxdepth: 1
copyright
- conventions
+ style-guide
authorintro
designdefense
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index 570cb2285..c3c2ba64c 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -22,7 +22,7 @@ the following sections.
.. sidebar:: Python Versions
As of this writing, :app:`Pyramid` is tested against Python 2.7,
- Python 3.4, Python 3.5, PyPy.
+ Python 3.4, Python 3.5, Python 3.6, and PyPy.
: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
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 47638579b..adad196e4 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -860,7 +860,7 @@ 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 Travis,
-and Jenkins on Python 2.7, Python 3.4, Python 3.5, and PyPy
+and Jenkins on Python 2.7, Python 3.4, Python 3.5, Python 3.6, 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
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst
index 45c706b0d..5dc7d8816 100644
--- a/docs/quick_tour.rst
+++ b/docs/quick_tour.rst
@@ -26,7 +26,7 @@ To save a little bit of typing and to be certain that we use the modules,
scripts, and packages installed in our virtual environment, we'll set an
environment variable, too.
-As an example, for Python 3.5+ on Linux:
+As an example, for Python 3.6+ on Linux:
.. parsed-literal::
@@ -729,7 +729,7 @@ This yields the following output.
collected 1 items
hello_world/tests.py .
- ------------- coverage: platform darwin, python 3.5.0-final-0 -------------
+ ------------- coverage: platform darwin, python 3.6.0-final-0 -------------
Name Stmts Miss Cover Missing
--------------------------------------------------------
hello_world/__init__.py 11 8 27% 11-23
diff --git a/docs/quick_tutorial/requirements.rst b/docs/quick_tutorial/requirements.rst
index afa8ed104..913e08a62 100644
--- a/docs/quick_tutorial/requirements.rst
+++ b/docs/quick_tutorial/requirements.rst
@@ -19,11 +19,11 @@ virtual environment.)
This *Quick Tutorial* is based on:
-* **Python 3.5**. Pyramid fully supports Python 3.4+ and Python 2.7+. This
- tutorial uses **Python 3.5** but runs fine under Python 2.7.
+* **Python 3.6**. Pyramid fully supports Python 3.4+ and Python 2.7+. This
+ tutorial uses **Python 3.6** but runs fine under Python 2.7.
* **venv**. We believe in virtual environments. For this tutorial, we use
- Python 3.5's built-in solution :term:`venv`. For Python 2.7, you can install
+ Python 3.6's built-in solution :term:`venv`. For Python 2.7, you can install
:term:`virtualenv`.
* **pip**. We use :term:`pip` for package management.
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
new file mode 100644
index 000000000..bdca45a06
--- /dev/null
+++ b/docs/style-guide.rst
@@ -0,0 +1,1258 @@
+.. _style-guide:
+
+Style Guide
+===========
+
+.. meta::
+ :description: This chapter describes how to edit, update, and build the Pyramid documentation.
+ :keywords: Pyramid, Style Guide
+
+
+.. _style-guide-introduction:
+
+Introduction
+------------
+
+This chapter provides details of how to contribute updates to the documentation following style guidelines and conventions. We provide examples, including reStructuredText code and its rendered output for both visual and technical reference.
+
+For coding style guidelines, see `Coding Style <http://docs.pylonsproject.org/en/latest/community/codestyle.html#coding-style>`_.
+
+
+.. _style-guide-contribute:
+
+How to update and contribute to documentation
+---------------------------------------------
+
+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>`_ and `Coding Style and Standards <http://docs.pylonsproject.org/en/latest/community/codestyle.html>`_.
+
+By building the documentation locally, you can preview the output before committing and pushing your changes to the repository. Follow the instructions for `Building documentation for a Pylons Project project <https://github.com/Pylons/pyramid/blob/master/contributing.md#building-documentation-for-a-pylons-project-project>`_. These instructions also include how to install packages required to build the documentation, and how to follow our recommended git workflow.
+
+When submitting a pull request for the first time in a project, sign `CONTRIBUTORS.txt <https://github.com/Pylons/pyramid/blob/master/CONTRIBUTORS.txt>`_ and commit it along with your pull request.
+
+
+.. _style-guide-file-conventions:
+
+Location, referencing, and naming of files
+------------------------------------------
+
+* reStructuredText (reST) files must be located in ``docs/`` and its subdirectories.
+* Image files must be located in ``docs/_static/``.
+* reST directives must refer to files either relative to the source file or absolute from the top source directory. For example, in ``docs/narr/source.rst``, you could refer to a file in a different directory as either ``.. include:: ../diff-dir/diff-source.rst`` or ``.. include:: /diff-dir/diff-source.rst``.
+* File names should be lower-cased and have words separated with either a hyphen "-" or an underscore "_".
+* reST files must have an extension of ``.rst``.
+* Image files may be any format but must have lower-cased file names and have standard file extensions that consist three letters (``.gif``, ``.jpg``, ``.png``, ``.svg``). ``.gif`` and ``.svg`` are not currently supported by PDF builders in Sphinx, but you can allow the Sphinx builder to automatically select the correct image format for the desired output by replacing the three-letter file extension with ``*``. For example:
+
+ .. code-block:: rst
+
+ .. image:: ../_static/pyramid_request_processing.
+
+ will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related `Stack Overflow post <http://stackoverflow.com/questions/6473660/using-sphinx-docs-how-can-i-specify-png-image-formats-for-html-builds-and-pdf-im/6486713#6486713>`_.
+
+
+.. _style-guide-table-of-contents-tree:
+
+Table of contents tree
+----------------------
+
+To insert a table of contents (TOC), use the ``toctree`` directive. Entries listed under the ``toctree`` directive follow :ref:`location conventions <style-guide-file-conventions>`. A numeric ``maxdepth`` option may be given to indicate the depth of the tree; by default, all levels are included.
+
+.. code-block:: rst
+
+ .. toctree::
+ :maxdepth: 2
+
+ narr/introduction
+ narr/install
+
+The above code renders as follows.
+
+.. toctree::
+ :maxdepth: 2
+
+ narr/introduction
+ narr/install
+
+Globbing can be used.
+
+.. code-block:: rst
+
+ .. toctree::
+ :maxdepth: 1
+ :glob:
+
+ pscripts/index
+ pscripts/*
+
+The above code renders as follows.
+
+.. toctree::
+ :maxdepth: 1
+ :glob:
+
+ pscripts/index
+ pscripts/*
+
+To notify Sphinx of the document hierarchy, but not insert links into the document at the location of the directive, use the option ``hidden``. This makes sense when you want to insert these links yourself, in a different style, or in the HTML sidebar.
+
+.. code-block:: rst
+
+ .. toctree::
+ :hidden:
+
+ quick_tour
+
+ * :doc:`quick_tour` gives an overview of the major features in Pyramid, covering a little about a lot.
+
+The above code renders as follows.
+
+.. toctree::
+ :hidden:
+
+ quick_tour
+
+* :doc:`quick_tour` gives an overview of the major features in Pyramid, covering a little about a lot.
+
+.. seealso:: Sphinx documentation of :ref:`toctree-directive`.
+
+
+.. _style-guide-glossary:
+
+Glossary
+--------
+
+A glossary defines terms used throughout the documentation.
+
+The glossary file must be named ``glossary.rst``. Its content must begin with the directive ``glossary``. An optional ``sorted`` argument should be used to sort the terms alphabetically when rendered, making it easier for the user to find a given term. Without the argument ``sorted``, terms will appear in the order of the ``glossary`` source file.
+
+.. code-block:: rst
+
+ .. glossary::
+ :sorted:
+
+ voom
+ Theoretically, the sound a parrot makes when four-thousand volts of electricity pass through it.
+
+ pining
+ What the Norwegien Blue does when it misses its homeland, e.g., pining for the fjords.
+
+The above code renders as follows.
+
+.. glossary::
+ :sorted:
+
+ voom
+ Theoretically, the sound a parrot makes when four-thousand volts of electricity pass through it.
+
+ pining
+ What the Norwegien Blue does when it misses its homeland, e.g., pining for the fjords.
+
+References to glossary terms use the ``term`` directive.
+
+.. code-block:: rst
+
+ :term:`voom`
+
+The above code renders as follows. Note it is hyperlinked, and when clicked it will take the user to the term in the Glossary and highlight the term.
+
+:term:`voom`
+
+
+.. _style-guide-section-structure:
+
+Section structure
+-----------------
+
+Each section, or a subdirectory of reST files, such as a tutorial, must contain an ``index.rst`` file. ``index.rst`` must contain the following.
+
+* A section heading. This will be visible in the table of contents.
+* A single paragraph describing this section.
+* A Sphinx ``toctree`` directive, with a ``maxdepth`` of 2. Each ``.rst`` file in the folder should be linked to this ``toctree``.
+
+ .. code-block:: rst
+
+ .. toctree::
+ :maxdepth: 2
+
+ chapter1
+ chapter2
+ chapter3
+
+
+.. _style-guide-page-structure:
+
+Page structure
+--------------
+
+Each page should contain in order the following.
+
+#. The main heading. This will be visible in the table of contents.
+
+ .. code-block:: rst
+
+ ================
+ The main heading
+ ================
+
+#. Meta tag information. The "meta" directive is used to specify HTML metadata stored in HTML META tags. "Metadata" is data about data, in this case data about web pages. Metadata is used to describe and classify web pages in the World Wide Web, in a form that is easy for search engines to extract and collate.
+
+ .. code-block:: rst
+
+ .. meta::
+ :description: This chapter describes how to edit, update, and build the Pyramid documentation.
+ :keywords: Pyramid, Style Guide
+
+ The above code renders as follows.
+
+ .. code-block:: xml
+
+ <meta content="This chapter describes how to edit, update, and build the Pyramid documentation." name="description" />
+ <meta content="Pyramid, Style Guide" name="keywords" />
+
+#. Introduction paragraph.
+
+ .. code-block:: rst
+
+ Introduction
+ ------------
+
+ This chapter is an introduction.
+
+#. Finally the content of the document page, consisting of reST elements such as headings, paragraphs, tables, and so on.
+
+
+.. _style-guide-page-content:
+
+Page content
+------------
+
+Within a page, content should adhere to specific guidelines.
+
+
+.. _style-guide-line-lengths:
+
+Line lengths
+^^^^^^^^^^^^
+
+Narrative documentation is not code, and should therefore not adhere to PEP8 or other line length conventions. When a translator sees only part of a sentence or paragraph, it makes it more difficult to translate the concept. Line lengths make ``diff`` more difficult. Text editors can soft wrap lines for display to avoid horizontal scrolling. We admit, we boofed it by using arbitrary 79-character line lengths in our own documentation, but we have seen the error of our ways and wish to correct this going forward.
+
+
+.. _style-guide-trailing-white-space:
+
+Trailing white spaces
+^^^^^^^^^^^^^^^^^^^^^
+
+* No trailing white spaces.
+* Always use a line feed or carriage return at the end of a file.
+
+
+.. _style-guide-indentation:
+
+Indentation
+^^^^^^^^^^^
+
+* Indent using four spaces, except for :ref:`nested lists <style-guide-lists>`.
+* Do not use tabs to indent.
+
+
+.. _style-guide-grammar-spelling-preferences:
+
+Grammar, spelling, and capitalization preferences
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation.
+
+========== =====
+Preferred Avoid
+========== =====
+add-on addon
+and so on etc.
+GitHub Github, github
+JavaScript Javascript, javascript
+plug-in plugin
+select check, tick (checkbox)
+such as like
+verify be sure
+========== =====
+
+
+.. _style-guide-headings:
+
+Headings
+^^^^^^^^
+
+Capitalize only the first letter in a heading (sentence-case), unless other words are proper nouns or acronyms, e.g., "Pyramid" or "HTML".
+
+For consistent heading characters throughout the documentation, follow the guidelines stated in the `Python Developer's Guide <https://docs.python.org/devguide/documenting.html#sections>`_. Specifically:
+
+* =, for sections
+* -, for subsections
+* ^, for subsubsections
+* ", for paragraphs
+
+As individual files do not have so-called "parts" or "chapters", the headings would be underlined with characters as shown.
+
+ .. code-block:: rst
+
+ ==================================
+ The main heading or web page title
+ ==================================
+
+ Heading Level 1
+ ---------------
+
+ Heading Level 2
+ ^^^^^^^^^^^^^^^
+
+ Heading Level 3
+ """""""""""""""
+
+Note, we do not render heading levels here because doing so causes a loss in page structure.
+
+
+.. _style-guide-paragraphs:
+
+Paragraphs
+^^^^^^^^^^
+
+A paragraph should be on one line. Paragraphs must be separated by two line feeds.
+
+
+.. _style-guide-links:
+
+Links
+^^^^^
+
+Use inline links to keep the context or link label together with the URL. Do not use targets and links at the end of the page, because the separation makes it difficult to update and translate. Here is an example of inline links, our required method.
+
+.. code-block:: rst
+
+ `TryPyramid <https://trypyramid.com>`_
+
+The above code renders as follows.
+
+`TryPyramid <https://TryPyramid.com>`_
+
+.. seealso:: See also :ref:`style-guide-cross-references` for generating links throughout the entire documentation.
+
+
+.. _style-guide-topic:
+
+Topic
+^^^^^
+
+A topic is similar to a block quote with a title, or a self-contained section with no subsections. Use the ``topic`` directive to indicate a self-contained idea that is separate from the flow of the document. Topics may occur anywhere a section or transition may occur. Body elements and topics may not contain nested topics.
+
+The directive's sole argument is interpreted as the topic title, and next line must be blank. All subsequent lines make up the topic body, interpreted as body elements.
+
+ .. code-block:: rst
+
+ .. topic:: Topic Title
+
+ Subsequent indented lines comprise
+ the body of the topic, and are
+ interpreted as body elements.
+
+The above code renders as follows.
+
+.. topic:: Topic Title
+
+ Subsequent indented lines comprise
+ the body of the topic, and are
+ interpreted as body elements.
+
+.. _style-guide-displaying-code:
+
+Displaying code
+^^^^^^^^^^^^^^^
+
+Code may be displayed in blocks or inline. You can include blocks of code from other source files. Blocks of code should use syntax highlighting, and may use line numbering or emphasis.
+
+.. seealso:: See also the Sphinx documentation for :ref:`code-examples`.
+
+
+.. _style-guide-syntax-highlighting:
+
+Syntax highlighting
+"""""""""""""""""""
+
+Sphinx does syntax highlighting of code blocks using the `Pygments <http://pygments.org/>`_ library.
+
+Do not use two colons "::" at the end of a line, followed by a blank line, then code. Always specify the language to be used for syntax highlighting by using a language argument in the ``code-block`` directive. Always indent the subsequent code.
+
+.. code-block:: rst
+
+ .. code-block:: python
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+XML:
+
+.. code-block:: rst
+
+ .. code-block:: xml
+
+ <somesnippet>Some XML</somesnippet>
+
+Unix shell commands are prefixed with a ``$`` character. (See :term:`venv` for the meaning of ``$VENV``.)
+
+.. code-block:: rst
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pip install -e .
+
+Windows commands are prefixed with a drive letter with an optional directory name. (See :term:`venv` for the meaning of ``%VENV%``.)
+
+.. code-block:: rst
+
+ .. code-block:: doscon
+
+ c:\> %VENV%\Scripts\pcreate -s starter MyProject
+
+cfg:
+
+.. code-block:: rst
+
+ .. code-block:: cfg
+
+ [some-part]
+ # A random part in the buildout
+ recipe = collective.recipe.foo
+ option = value
+
+ini:
+
+.. code-block:: rst
+
+ .. code-block:: ini
+
+ [nosetests]
+ match=^test
+ where=pyramid
+ nocapture=1
+
+Interactive Python:
+
+.. code-block:: rst
+
+ .. code-block:: pycon
+
+ >>> class Foo:
+ ... bar = 100
+ ...
+ >>> f = Foo()
+ >>> f.bar
+ 100
+ >>> f.bar / 0
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ ZeroDivisionError: integer division or modulo by zero
+
+If syntax highlighting is not enabled for your code block, you probably have a syntax error and Pygments will fail silently.
+
+View the `full list of lexers and associated short names <http://pygments.org/docs/lexers/>`_.
+
+
+.. _style-guide-parsed-literals:
+
+Parsed literals
+"""""""""""""""
+
+Parsed literals are used to render, for example, a specific version number of the application in code blocks. Use the directive ``parsed-literal``. Note that syntax highlighting is not supported and code is rendered as plain text.
+
+.. code-block:: rst
+
+ .. parsed-literal::
+
+ $ $VENV/bin/pip install "pyramid==\ |release|\ "
+
+The above code renders as follows.
+
+.. parsed-literal::
+
+ $ $VENV/bin/pip install "pyramid==\ |release|\ "
+
+
+.. _style-guide-long-commands:
+
+Displaying long commands
+""""""""""""""""""""""""
+
+When a command that should be typed on one line is too long to fit on the displayed width of a page, the backslash character ``\`` is used to indicate that the subsequent printed line should be part of the command:
+
+.. code-block:: rst
+
+ .. code-block:: bash
+
+ $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \
+ --cov=tutorial -q
+
+
+.. _style-guide-code-block-options:
+
+Code block options
+""""""""""""""""""
+
+To emphasize lines (give the appearance that a highlighting pen has been used on the code), use the ``emphasize-lines`` option. The argument passed to ``emphasize-lines`` must be a comma-separated list of either single or ranges of line numbers.
+
+.. code-block:: rst
+
+ .. code-block:: python
+ :emphasize-lines: 1,3
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+The above code renders as follows.
+
+.. code-block:: python
+ :emphasize-lines: 1,3
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+To display a code block with line numbers, use the ``linenos`` option.
+
+.. code-block:: rst
+
+ .. code-block:: python
+ :linenos:
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+The above code renders as follows.
+
+.. code-block:: python
+ :linenos:
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+Code blocks may be given a caption, which may serve as a filename or other description, using the ``caption`` option. They may also be given a ``name`` option, providing an implicit target name that can be referenced by using ``ref`` (see :ref:`style-guide-cross-referencing-arbitrary-locations`).
+
+.. code-block:: rst
+
+ .. code-block:: python
+ :caption: sample.py
+ :name: sample-py
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+The above code renders as follows.
+
+.. code-block:: python
+ :caption: sample.py
+ :name: sample-py
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+To specify the starting number to use for line numbering, use the ``lineno-start`` directive.
+
+.. code-block:: rst
+
+ .. code-block:: python
+ :lineno-start: 2
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+The above code renders as follows. As you can see, ``lineno-start`` is not altogether accurate.
+
+.. code-block:: python
+ :lineno-start: 2
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+
+.. _style-guide-includes:
+
+Includes
+""""""""
+
+Longer displays of verbatim text may be included by storing the example text in an external file containing only plain text or code. The file may be included using the ``literalinclude`` directive. The file name follows the conventions of :ref:`style-guide-file-conventions`.
+
+.. code-block:: rst
+
+ .. literalinclude:: narr/helloworld.py
+ :language: python
+
+The above code renders as follows.
+
+.. literalinclude:: narr/helloworld.py
+ :language: python
+
+Like code blocks, ``literalinclude`` supports the following options.
+
+* ``language`` to select a language for syntax highlighting
+* ``linenos`` to switch on line numbers
+* ``lineno-start`` to specify the starting number to use for line numbering
+* ``emphasize-lines`` to emphasize particular lines
+
+.. code-block:: rst
+
+ .. literalinclude:: narr/helloworld.py
+ :language: python
+ :linenos:
+ :lineno-start: 11
+ :emphasize-lines: 1,6-7,9-
+
+The above code renders as follows. Note that ``lineno-start`` and ``emphasize-lines`` do not align. The former displays numbering starting from the *arbitrarily provided value*, whereas the latter emphasizes the line numbers of the *source file*.
+
+.. literalinclude:: narr/helloworld.py
+ :language: python
+ :linenos:
+ :lineno-start: 11
+ :emphasize-lines: 1,6-7,9-
+
+``literalinclude`` also supports including only parts of a file.
+
+If the source code is a Python module, you can select a class, function, or method to include using the ``pyobject`` option.
+
+.. code-block:: rst
+
+ .. literalinclude:: narr/helloworld.py
+ :language: python
+ :pyobject: hello_world
+
+The above code renders as follows. It returns the function ``hello_world`` in the source file.
+
+.. literalinclude:: narr/helloworld.py
+ :language: python
+ :pyobject: hello_world
+
+Another way to control which part of the file is included is to use the ``start-after`` and ``end-before`` options (or only one of them). If ``start-after`` is given as a string option, only lines that follow the first line containing that string are included. If ``end-before`` is given as a string option, only lines that precede the first lines containing that string are included.
+
+.. code-block:: rst
+
+ .. literalinclude:: narr/helloworld.py
+ :language: python
+ :start-after: from pyramid.response import Response
+ :end-before: if __name__ == '__main__':
+
+The above code renders as follows.
+
+.. literalinclude:: narr/helloworld.py
+ :language: python
+ :start-after: from pyramid.response import Response
+ :end-before: if __name__ == '__main__':
+
+You can specify exactly which lines to include by giving a ``lines`` option.
+
+.. code-block:: rst
+
+ .. literalinclude:: narr/helloworld.py
+ :language: python
+ :lines: 6-7
+
+The above code renders as follows.
+
+.. literalinclude:: narr/helloworld.py
+ :language: python
+ :lines: 6-7
+
+When specifying particular parts of a file to display, it can be useful to display exactly which lines are being presented. This can be done using the ``lineno-match`` option.
+
+.. code-block:: rst
+
+ .. literalinclude:: narr/helloworld.py
+ :language: python
+ :lines: 6-7
+ :lineno-match:
+
+The above code renders as follows.
+
+.. literalinclude:: narr/helloworld.py
+ :language: python
+ :lines: 6-7
+ :lineno-match:
+
+Out of all the ways to include parts of a file, ``pyobject`` is the most preferred option because if you change your code and add or remove lines, you don't need to adjust line numbering, whereas with ``lines`` you would have to adjust. ``start-after`` and ``end-before`` are less desirable because they depend on source code not changing. Alternatively you can insert comments into your source code to act as the delimiters, but that just adds comments that have nothing to do with the functionality of your code.
+
+Above all with includes, if you use line numbering, it's much preferred to use ``lineno-match`` over ``linenos`` with ``lineno-start`` because it "just works" without thinking and with less markup.
+
+
+.. _style-guide-inline-code:
+
+Inline code
+"""""""""""
+
+Inline code is surrounded by double backtick marks. Literals, filenames, and function arguments are presented using this style.
+
+.. code-block:: rst
+
+ Install requirements for building documentation: ``pip install -e ".[docs]"``
+
+The above code renders as follows.
+
+Install requirements for building documentation: ``pip install -e ".[docs]"``
+
+
+.. _style-guide-rest-block-markup:
+
+reST block markup
+-----------------
+
+This section contains miscellaneous reST block markup for items not already covered.
+
+
+.. _style-guide-lists:
+
+Lists
+^^^^^
+
+Bulleted lists use an asterisk "``*``".
+
+.. code-block:: rst
+
+ * This is an item in a bulleted list.
+ * This is another item in a bulleted list.
+
+The above code renders as follows.
+
+* This is an item in a bulleted list.
+* This is another item in a bulleted list.
+
+Numbered lists should use a number sign followed by a period "``#.``" and will be numbered automatically.
+
+.. code-block:: rst
+
+ #. This is an item in a numbered list.
+ #. This is another item in a numbered list.
+
+The above code renders as follows.
+
+#. This is an item in a numbered list.
+#. This is another item in a numbered list.
+
+The appearance of nested lists can be created by separating the child lists from their parent list by blank lines, and indenting by two spaces. Note that Sphinx renders the reST markup not as nested HTML lists, but instead merely indents the children using ``<blockquote>``.
+
+.. code-block:: rst
+
+ #. This is a list item in the parent list.
+ #. This is another list item in the parent list.
+
+ #. This is a list item in the child list.
+ #. This is another list item in the child list.
+
+ #. This is one more list item in the parent list.
+
+The above code renders as follows.
+
+#. This is a list item in the parent list.
+#. This is another list item in the parent list.
+
+ #. This is a list item in the child list.
+ #. This is another list item in the child list.
+
+#. This is one more list item in the parent list.
+
+
+.. _style-guide-tables:
+
+Tables
+^^^^^^
+
+Two forms of tables are supported, `simple <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#simple-tables>`_ and `grid <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#grid-tables>`_.
+
+Simple tables require less markup but have fewer features and some constraints compared to grid tables. The right-most column in simple tables is unbound to the length of the underline in the column header.
+
+.. code-block:: rst
+
+ ===== =====
+ col 1 col 2
+ ===== =====
+ 1 Second column of row 1.
+ 2 Second column of row 2.
+ Second line of paragraph.
+ 3 * Second column of row 3.
+
+ * Second item in bullet
+ list (row 3, column 2).
+ \ Row 4; column 1 will be empty.
+ ===== =====
+
+The above code renders as follows.
+
+===== =====
+col 1 col 2
+===== =====
+1 Second column of row 1.
+2 Second column of row 2.
+ Second line of paragraph.
+3 * Second column of row 3.
+
+ * Second item in bullet
+ list (row 3, column 2).
+\ Row 4; column 1 will be empty.
+===== =====
+
+Grid tables have much more cumbersome markup, although Emacs' table mode may lessen the tedium.
+
+.. code-block:: rst
+
+ +------------------------+------------+----------+----------+
+ | Header row, column 1 | Header 2 | Header 3 | Header 4 |
+ | (header rows optional) | | | |
+ +========================+============+==========+==========+
+ | body row 1, column 1 | column 2 | column 3 | column 4 |
+ +------------------------+------------+----------+----------+
+ | body row 2 | Cells may span columns. |
+ +------------------------+------------+---------------------+
+ | body row 3 | Cells may | * Table cells |
+ +------------------------+ span rows. | * contain |
+ | body row 4 | | * body elements. |
+ +------------------------+------------+---------------------+
+
+The above code renders as follows.
+
++------------------------+------------+----------+----------+
+| Header row, column 1 | Header 2 | Header 3 | Header 4 |
+| (header rows optional) | | | |
++========================+============+==========+==========+
+| body row 1, column 1 | column 2 | column 3 | column 4 |
++------------------------+------------+----------+----------+
+| body row 2 | Cells may span columns. |
++------------------------+------------+---------------------+
+| body row 3 | Cells may | * Table cells |
++------------------------+ span rows. | * contain |
+| body row 4 | | * body elements. |
++------------------------+------------+---------------------+
+
+
+.. _style-guide-feature-versioning:
+
+Feature versioning
+^^^^^^^^^^^^^^^^^^
+
+Three directives designate the version in which something is added, changed, or deprecated in the project.
+
+
+.. _style-guide-version-added:
+
+Version added
+"""""""""""""
+
+To indicate the version in which a feature is added to a project, use the ``versionadded`` directive. If the feature is an entire module, then the directive should be placed at the top of the module section before any prose.
+
+The first argument is the version. An optional second argument must appear upon a subsequent line, without blank lines in between, and indented.
+
+.. code-block:: rst
+
+ .. versionadded:: 1.1
+ :func:`pyramid.paster.bootstrap`
+
+The above code renders as follows.
+
+.. versionadded:: 1.1
+ :func:`pyramid.paster.bootstrap`
+
+
+.. _style-guide-version-changed:
+
+Version changed
+"""""""""""""""
+
+To indicate the version in which a feature is changed in a project, use the ``versionchanged`` directive. Its arguments are the same as ``versionadded``.
+
+.. code-block:: rst
+
+ .. versionchanged:: 1.8
+ Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement.
+
+The above code renders as follows.
+
+.. versionchanged:: 1.8
+ Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement.
+
+
+.. _style-guide-deprecated:
+
+Deprecated
+""""""""""
+
+Similar to ``versionchanged``, ``deprecated`` describes when the feature was deprecated. An explanation can also be given, for example, to inform the reader what should be used instead.
+
+.. code-block:: rst
+
+ .. deprecated:: 1.7
+ Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised.
+
+The above code renders as follows.
+
+.. deprecated:: 1.7
+ Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised.
+
+
+.. _style-guide-danger:
+
+Danger
+^^^^^^
+
+Danger represents critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing".
+
+.. code-block:: rst
+
+ .. danger::
+
+ This is danger or an error.
+
+The above code renders as follows.
+
+.. danger::
+
+ This is danger or an error.
+
+.. todo::
+
+ The style for ``danger`` and ``error`` has not yet been created.
+
+
+.. _style-guide-warnings:
+
+Warnings
+^^^^^^^^
+
+Warnings represent limitations and advice related to a topic or concept.
+
+.. code-block:: rst
+
+ .. warning::
+
+ This is a warning.
+
+The above code renders as follows.
+
+.. warning::
+
+ This is a warning.
+
+
+.. _style-guide-notes:
+
+Notes
+^^^^^
+
+Notes represent additional information related to a topic or concept.
+
+.. code-block:: rst
+
+ .. note::
+
+ This is a note.
+
+The above code renders as follows.
+
+.. note::
+
+ This is a note.
+
+
+.. _style-guide-see-also:
+
+See also
+^^^^^^^^
+
+"See also" messages refer to topics that are related to the current topic, but have a narrative tone to them instead of merely a link without explanation. "See also" is rendered in a block as well, so that it stands out for the reader's attention.
+
+.. code-block:: rst
+
+ .. seealso::
+
+ See :ref:`Quick Tutorial section on Requirements <qtut_requirements>`.
+
+The above code renders as follows.
+
+.. seealso::
+
+ See :ref:`Quick Tutorial section on Requirements <qtut_requirements>`.
+
+
+.. _style-guide-todo:
+
+Todo
+^^^^
+
+Todo items designated tasks that require further work.
+
+.. code-block:: rst
+
+ .. todo::
+
+ This is a todo item.
+
+The above code renders as follows.
+
+.. todo::
+
+ This is a todo item.
+
+.. todo::
+
+ The todo style is not yet implemented and needs further work.
+
+
+.. _style-guide-comments:
+
+Comments
+^^^^^^^^
+
+Comments of the documentation within the documentation may be generated with two periods ``..``. Comments are not rendered, but provide information to documentation authors.
+
+.. code-block:: rst
+
+ .. This is an example comment.
+
+
+.. _style-guide-rest-inline-markup:
+
+reST inline markup
+------------------
+
+This section contains miscellaneous reST inline markup for items not already covered. Within a block of content, inline markup is useful to apply styles and links to other files.
+
+
+.. _style-guide-italics:
+
+Italics
+^^^^^^^
+
+.. code-block:: rst
+
+ This *word* is italicized.
+
+The above code renders as follows.
+
+This *word* is italicized.
+
+
+.. _style-guide-strong:
+
+Strong
+^^^^^^
+
+.. code-block:: rst
+
+ This **word** is in bold text.
+
+The above code renders as follows.
+
+This **word** is in bold text.
+
+.. seealso::
+
+ See also the Sphinx documentation for the :ref:`rst-primer`.
+
+
+.. _style-guide-cross-references:
+
+Cross-references
+^^^^^^^^^^^^^^^^
+
+To create cross-references to a document, arbitrary location, object, or other items, use variations of the following syntax.
+
+* ``:role:`target``` creates a link to the item named ``target`` of the type indicated by ``role``, with the link's text as the title of the target. ``target`` may need to be disambiguated between documentation sets linked through intersphinx, in which case the syntax would be ``deform:overview``.
+* ``:role:`~target``` displays the link as only the last component of the target.
+* ``:role:`title <target>``` creates a custom title, instead of the default title of the target.
+
+
+.. _style-guide-cross-referencing-documents:
+
+Cross-referencing documents
+"""""""""""""""""""""""""""
+
+To link to pages within this documentation:
+
+.. code-block:: rst
+
+ :doc:`quick_tour`
+
+The above code renders as follows.
+
+:doc:`quick_tour`
+
+
+.. _style-guide-cross-referencing-arbitrary-locations:
+
+Cross-referencing arbitrary locations
+"""""""""""""""""""""""""""""""""""""
+
+To support cross-referencing to arbitrary locations in any document and between documentation sets via intersphinx, the standard reST labels are used. For this to work, label names must be unique throughout the entire documentation including externally linked intersphinx references. There are two ways in which you can refer to labels, if they are placed directly before a section title, a figure, or table with a caption, or at any other location. The following section has a label with the syntax ``.. _label_name:`` followed by the section title.
+
+.. code-block:: rst
+
+ .. _i18n_chapter:
+
+ Internationalization and Localization
+ =====================================
+
+To generate a link to that section with its title, use the following syntax.
+
+.. code-block:: rst
+
+ :ref:`i18n_chapter`
+
+The above code renders as follows.
+
+:ref:`i18n_chapter`
+
+The same syntax works figures and tables with captions.
+
+For labels that are not placed as mentioned, the link must be given an explicit title, such as ``:ref:`Link title <label-name>```.
+
+.. seealso:: See also the Sphinx documentation, :ref:`inline-markup`.
+
+
+.. _style-guide-cross-referencing-python:
+
+Python modules, classes, methods, and functions
+"""""""""""""""""""""""""""""""""""""""""""""""
+
+Python module names use the ``mod`` directive, with the module name as the argument.
+
+.. code-block:: rst
+
+ :mod:`pyramid.config`
+
+The above code renders as follows.
+
+:mod:`pyramid.config`
+
+Python class names use the ``class`` directive, with the class name as the argument.
+
+.. code-block:: rst
+
+ :class:`pyramid.config.Configurator`
+
+The above code renders as follows.
+
+:class:`pyramid.config.Configurator`
+
+Python method names use the ``meth`` directive, with the method name as the argument.
+
+.. code-block:: rst
+
+ :meth:`pyramid.config.Configurator.add_view`
+
+The above code renders as follows.
+
+:meth:`pyramid.config.Configurator.add_view`
+
+Python function names use the ``func`` directive, with the function name as the argument.
+
+.. code-block:: rst
+
+ :func:`pyramid.renderers.render_to_response`
+
+The above code renders as follows.
+
+:func:`pyramid.renderers.render_to_response`
+
+Note that you can use the ``~`` prefix to show only the last segment of a Python object's name. We prefer not to use the ``.`` prefix, even though it may seem to be a convenience to documentation authors, because Sphinx might generate an error if it cannot disambiguate the reference.
+
+.. code-block:: rst
+
+ :func:`~pyramid.renderers.render_to_response`
+
+The above code renders as follows.
+
+:func:`~pyramid.renderers.render_to_response`
+
+
+.. _style-guide-role-app-pyramid:
+
+The role ``:app:`Pyramid```
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+We use the special role ``app`` to refer to the application "Pyramid".
+
+.. code-block:: rst
+
+ :app:`Pyramid`
+
+The above code renders as follows.
+
+:app:`Pyramid`
+
+
+.. _style-guide-sphinx-extensions:
+
+Sphinx extensions
+-----------------
+
+We use several Sphinx extensions to add features to our documentation. Extensions need to be enabled and configured in ``docs/conf.py`` before they can be used.
+
+
+.. _style-guide-sphinx-extension-autodoc:
+
+:mod:`sphinx.ext.autodoc`
+-------------------------
+
+API documentation uses the Sphinx extension :mod:`sphinx.ext.autodoc` to include documentation from docstrings.
+
+See the source of any documentation within the ``docs/api/`` directory for conventions and usage, as well as the Sphinx extension's :mod:`documentation <sphinx.ext.autodoc>`.
+
+
+.. _style-guide-sphinx-extension-doctest:
+
+:mod:`sphinx.ext.doctest`
+-------------------------
+
+:mod:`sphinx.ext.doctest` allows you to test code snippets in the documentation in a natural way. It works by collecting specially-marked up code blocks and running them as doctest tests. We have only a few tests in our Pyramid documentation which can be found in ``narr/sessions.rst`` and ``narr/hooks.rst``.
+
+
+.. _style-guide-sphinx-extension-intersphinx:
+
+:mod:`sphinx.ext.intersphinx`
+-----------------------------
+
+:mod:`sphinx.ext.intersphinx` generates links to the documentation of objects in other projects.
+
+
+.. _style-guide-sphinx-extension-todo:
+
+:mod:`sphinx.ext.todo`
+----------------------
+
+:mod:`sphinx.ext.todo` adds support for todo items.
+
+
+.. _style-guide-sphinx-extension-viewcode:
+
+:mod:`sphinx.ext.viewcode`
+--------------------------
+
+:mod:`sphinx.ext.viewcode` looks at your Python object descriptions and tries to find the source files where the objects are contained. When found, a separate HTML page will be output for each module with a highlighted version of the source code, and a link will be added to all object descriptions that leads to the source code of the described object. A link back from the source to the description will also be inserted.
+
+
+.. _style-guide-sphinx-extension-repoze-sphinx-autointerface:
+
+`repoze.sphinx.autointerface <https://pypi.python.org/pypi/repoze.sphinx.autointerface>`_
+-----------------------------------------------------------------------------------------
+
+`repoze.sphinx.autointerface <https://pypi.python.org/pypi/repoze.sphinx.autointerface>`_ auto-generates API docs from Zope interfaces.
+
+
+.. _style-guide-script-documentation:
+
+Script documentation
+--------------------
+
+We currently use `sphinxcontrib-programoutput <https://pypi.python.org/pypi/sphinxcontrib-programoutput>`_ to generate program output of the p* scripts. It is no longer maintained and may cause future builds of the documentation to fail.
+
+.. todo::
+
+ See `issue #2804 <https://github.com/Pylons/pyramid/issues/2804>`_ for further discussion.
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index 44097b35b..67af83b25 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -18,6 +18,7 @@ require permission, instead of a default "403 Forbidden" page.
We will implement the access control with the following steps:
+* Add password hashing dependencies.
* Add users and groups (``security.py``, a new module).
* Add an :term:`ACL` (``models.py``).
* Add an :term:`authentication policy` and an :term:`authorization policy`
@@ -38,11 +39,32 @@ Then we will add the login and logout feature:
Access control
--------------
+
+Add dependencies
+~~~~~~~~~~~~~~~~
+
+Just like in :ref:`wiki_defining_views`, we need a new dependency. We need to add the `bcrypt <https://pypi.python.org/pypi/bcrypt>`_ package, to our tutorial package's ``setup.py`` file by assigning this dependency to the ``requires`` parameter in the ``setup()`` function.
+
+Open ``setup.py`` and edit it to look like the following:
+
+.. literalinclude:: src/authorization/setup.py
+ :linenos:
+ :emphasize-lines: 21
+ :language: python
+
+Only the highlighted line needs to be added.
+
+Do not forget to run ``pip install -e .`` just like in :ref:`wiki-running-pip-install`.
+
+.. note::
+
+ We are using the ``bcrypt`` package from PyPI to hash our passwords securely. There are other one-way hash algorithms for passwords if bcrypt is an issue on your system. Just make sure that it's an algorithm approved for storing passwords versus a generic one-way hash.
+
+
Add users and groups
~~~~~~~~~~~~~~~~~~~~
-Create a new ``tutorial/security.py`` module with the
-following content:
+Create a new ``tutorial/security.py`` module with the following content:
.. literalinclude:: src/authorization/tutorial/security.py
:linenos:
@@ -61,7 +83,20 @@ 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
+There are two helper methods that will help us later to authenticate users.
+The first is ``hash_password`` which takes a raw password and transforms it using
+bcrypt into an irreversible representation, a process known as "hashing". The
+second method, ``check_password``, will allow us to compare the hashed value of the
+submitted password against the hashed value of the password stored in the user's
+record. If the two hashed values match, then the submitted
+password is valid, and we can authenticate the user.
+
+We hash passwords so that it is impossible to decrypt and use them to
+authenticate in the application. If we stored passwords foolishly in clear text,
+then anyone with access to the database could retrieve any password to authenticate
+as any user.
+
+In a production system, user and group data will most often be saved and come from a
database, but here we use "dummy" data to represent user and groups sources.
Add an ACL
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index ac94d8059..3859d2cad 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -52,6 +52,7 @@ Open ``setup.py`` and edit it to look like the following:
Only the highlighted line needs to be added.
+.. _wiki-running-pip-install:
Running ``pip install -e .``
============================
diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst
index 03e183739..ec79a4e9c 100644
--- a/docs/tutorials/wiki/installation.rst
+++ b/docs/tutorials/wiki/installation.rst
@@ -66,7 +66,7 @@ Python 2.7:
c:\> c:\Python27\Scripts\virtualenv %VENV%
-Python 3.5:
+Python 3.6:
.. code-block:: doscon
@@ -310,13 +310,13 @@ If successful, you will see output something like this:
.. code-block:: bash
======================== test session starts ========================
- platform Python 3.5.1, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
+ platform Python 3.6.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: /Users/stevepiercy/projects/pyramidtut/tutorial, inifile:
plugins: cov-2.2.1
collected 1 items
tutorial/tests.py .
- ------------------ coverage: platform Python 3.5.1 ------------------
+ ------------------ coverage: platform Python 3.6.0 ------------------
Name Stmts Miss Cover Missing
----------------------------------------------------
tutorial/__init__.py 12 7 42% 7-8, 14-18
diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py
index beeed75c9..68e3c0abd 100644
--- a/docs/tutorials/wiki/src/authorization/setup.py
+++ b/docs/tutorials/wiki/src/authorization/setup.py
@@ -18,6 +18,7 @@ requires = [
'ZODB3',
'waitress',
'docutils',
+ 'bcrypt',
]
tests_require = [
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/security.py b/docs/tutorials/wiki/src/authorization/tutorial/security.py
index d88c9c71f..cbb3acd5d 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/security.py
+++ b/docs/tutorials/wiki/src/authorization/tutorial/security.py
@@ -1,5 +1,18 @@
-USERS = {'editor':'editor',
- 'viewer':'viewer'}
+import bcrypt
+
+
+def hash_password(pw):
+ hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt())
+ # return unicode instead of bytes because databases handle it better
+ return hashed_pw.decode('utf-8')
+
+def check_password(expected_hash, pw):
+ if expected_hash is not None:
+ return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8'))
+ return False
+
+USERS = {'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer')}
GROUPS = {'editor':['group:editors']}
def groupfinder(userid, request):
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py
index c271d2cc1..e4560dfe1 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/views.py
+++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py
@@ -14,7 +14,7 @@ from pyramid.security import (
)
-from .security import USERS
+from .security import USERS, check_password
from .models import Page
# regular expression used to find WikiWords
@@ -94,7 +94,7 @@ def login(request):
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
- if USERS.get(login) == password:
+ if check_password(USERS.get(login), password):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py
index beeed75c9..68e3c0abd 100644
--- a/docs/tutorials/wiki/src/tests/setup.py
+++ b/docs/tutorials/wiki/src/tests/setup.py
@@ -18,6 +18,7 @@ requires = [
'ZODB3',
'waitress',
'docutils',
+ 'bcrypt',
]
tests_require = [
diff --git a/docs/tutorials/wiki/src/tests/tutorial/security.py b/docs/tutorials/wiki/src/tests/tutorial/security.py
index d88c9c71f..cbb3acd5d 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/security.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/security.py
@@ -1,5 +1,18 @@
-USERS = {'editor':'editor',
- 'viewer':'viewer'}
+import bcrypt
+
+
+def hash_password(pw):
+ hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt())
+ # return unicode instead of bytes because databases handle it better
+ return hashed_pw.decode('utf-8')
+
+def check_password(expected_hash, pw):
+ if expected_hash is not None:
+ return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8'))
+ return False
+
+USERS = {'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer')}
GROUPS = {'editor':['group:editors']}
def groupfinder(userid, request):
diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py
index 04beaea44..098e9c1bd 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/tests.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py
@@ -122,6 +122,17 @@ class EditPageTests(unittest.TestCase):
self.assertEqual(response.location, 'http://example.com/')
self.assertEqual(context.data, 'Hello yo!')
+class SecurityTests(unittest.TestCase):
+ def test_hashing(self):
+ from .security import hash_password, check_password
+ password = 'secretpassword'
+ hashed_password = hash_password(password)
+ self.assertTrue(check_password(hashed_password, password))
+
+ self.assertFalse(check_password(hashed_password, 'attackerpassword'))
+
+ self.assertFalse(check_password(None, password))
+
class FunctionalTests(unittest.TestCase):
viewer_login = '/login?login=viewer&password=viewer' \
diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py
index c271d2cc1..e4560dfe1 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/views.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/views.py
@@ -14,7 +14,7 @@ from pyramid.security import (
)
-from .security import USERS
+from .security import USERS, check_password
from .models import Page
# regular expression used to find WikiWords
@@ -94,7 +94,7 @@ def login(request):
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
- if USERS.get(login) == password:
+ if check_password(USERS.get(login), password):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index 75d5d4abd..fa990fb01 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -66,7 +66,7 @@ Python 2.7:
c:\> c:\Python27\Scripts\virtualenv %VENV%
-Python 3.5:
+Python 3.6:
.. code-block:: doscon
@@ -327,13 +327,13 @@ If successful, you will see output something like this:
.. code-block:: bash
======================== test session starts ========================
- platform Python 3.5.1, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
+ platform Python 3.6.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: /Users/stevepiercy/projects/pyramidtut/tutorial, inifile:
plugins: cov-2.2.1
collected 2 items
tutorial/tests.py ..
- ------------------ coverage: platform Python 3.5.1 ------------------
+ ------------------ coverage: platform Python 3.6.0 ------------------
Name Stmts Miss Cover Missing
----------------------------------------------------------------
tutorial/__init__.py 8 6 25% 7-12
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
index f3c0a6fef..c860ef8cf 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
@@ -28,6 +28,7 @@ def usage(argv):
def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
+ return
config_uri = argv[1]
options = parse_vars(argv[2:])
setup_logging(config_uri)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py
index 715768b2e..0250e71c9 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py
@@ -11,6 +11,9 @@ class FunctionalTests(unittest.TestCase):
basic_wrong_login = (
'/login?login=basic&password=incorrect'
'&next=FrontPage&form.submitted=Login')
+ basic_login_no_next = (
+ '/login?login=basic&password=basic'
+ '&form.submitted=Login')
editor_login = (
'/login?login=editor&password=editor'
'&next=FrontPage&form.submitted=Login')
@@ -68,6 +71,10 @@ class FunctionalTests(unittest.TestCase):
res = self.testapp.get(self.basic_login, status=302)
self.assertEqual(res.location, 'http://localhost/FrontPage')
+ def test_successful_log_in_no_next(self):
+ res = self.testapp.get(self.basic_login_no_next, status=302)
+ self.assertEqual(res.location, 'http://localhost/')
+
def test_failed_log_in(self):
res = self.testapp.get(self.basic_wrong_login, status=200)
self.assertTrue(b'login' in res.body)
@@ -120,3 +127,8 @@ class FunctionalTests(unittest.TestCase):
self.testapp.get(self.editor_login, status=302)
res = self.testapp.get('/FrontPage', status=200)
self.assertTrue(b'FrontPage' in res.body)
+
+ def test_redirect_to_edit_for_existing_page(self):
+ self.testapp.get(self.editor_login, status=302)
+ res = self.testapp.get('/add_page/FrontPage', status=302)
+ self.assertTrue(b'FrontPage' in res.body)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py
new file mode 100644
index 000000000..97511d5e8
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py
@@ -0,0 +1,20 @@
+import mock
+import unittest
+
+
+class TestInitializeDB(unittest.TestCase):
+
+ @mock.patch('tutorial.scripts.initializedb.sys')
+ def test_usage(self, mocked_sys):
+ from ..scripts.initializedb import main
+ main(argv=['foo'])
+ mocked_sys.exit.assert_called_with(1)
+
+ @mock.patch('tutorial.scripts.initializedb.get_tm_session')
+ @mock.patch('tutorial.scripts.initializedb.sys')
+ def test_run(self, mocked_sys, mocked_session):
+ from ..scripts.initializedb import main
+ main(argv=['foo', 'development.ini'])
+ mocked_session.assert_called_once()
+
+
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py
new file mode 100644
index 000000000..4c3b72946
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py
@@ -0,0 +1,21 @@
+import mock
+import unittest
+
+
+class TestMyAuthenticationPolicy(unittest.TestCase):
+
+ def test_no_user(self):
+ request = mock.Mock()
+ request.user = None
+
+ from ..security import MyAuthenticationPolicy
+ policy = MyAuthenticationPolicy(None)
+ self.assertEqual(policy.authenticated_userid(request), None)
+
+ def test_authenticated_user(self):
+ request = mock.Mock()
+ request.user.id = 'foo'
+
+ from ..security import MyAuthenticationPolicy
+ policy = MyAuthenticationPolicy(None)
+ self.assertEqual(policy.authenticated_userid(request), 'foo')
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py
new file mode 100644
index 000000000..9490ac990
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py
@@ -0,0 +1,67 @@
+import unittest
+import transaction
+
+from pyramid import testing
+
+
+class BaseTest(unittest.TestCase):
+
+ def setUp(self):
+ from ..models import get_tm_session
+ self.config = testing.setUp(settings={
+ 'sqlalchemy.url': 'sqlite:///:memory:'
+ })
+ self.config.include('..models')
+ self.config.include('..routes')
+
+ session_factory = self.config.registry['dbsession_factory']
+ self.session = get_tm_session(session_factory, transaction.manager)
+
+ self.init_database()
+
+ def init_database(self):
+ from ..models.meta import Base
+ session_factory = self.config.registry['dbsession_factory']
+ engine = session_factory.kw['bind']
+ Base.metadata.create_all(engine)
+
+ def tearDown(self):
+ testing.tearDown()
+ transaction.abort()
+
+ def makeUser(self, name, role):
+ from ..models import User
+ return User(name=name, role=role)
+
+
+class TestSetPassword(BaseTest):
+
+ def test_password_hash_saved(self):
+ user = self.makeUser(name='foo', role='bar')
+ self.assertFalse(user.password_hash)
+
+ user.set_password('secret')
+ self.assertTrue(user.password_hash)
+
+
+class TestCheckPassword(BaseTest):
+
+ def test_password_hash_not_set(self):
+ user = self.makeUser(name='foo', role='bar')
+ self.assertFalse(user.password_hash)
+
+ self.assertFalse(user.check_password('secret'))
+
+ def test_correct_password(self):
+ user = self.makeUser(name='foo', role='bar')
+ user.set_password('secret')
+ self.assertTrue(user.password_hash)
+
+ self.assertTrue(user.check_password('secret'))
+
+ def test_incorrect_password(self):
+ user = self.makeUser(name='foo', role='bar')
+ user.set_password('secret')
+ self.assertTrue(user.password_hash)
+
+ self.assertFalse(user.check_password('incorrect'))
diff --git a/docs/typographical-conventions.rst b/docs/typographical-conventions.rst
new file mode 100644
index 000000000..19894775b
--- /dev/null
+++ b/docs/typographical-conventions.rst
@@ -0,0 +1,338 @@
+.. _typographical-conventions:
+
+Typographical Conventions
+=========================
+
+.. meta::
+ :description: This chapter describes typographical conventions used in the Pyramid documentation.
+ :keywords: Pyramid, Typographical Conventions
+
+
+.. _typographical-conventions-introduction:
+
+Introduction
+------------
+
+This chapter describes typographical conventions used in the Pyramid documentation. Documentation authors and contributors should review the :ref:`style-guide`.
+
+
+.. _typographical-conventions-glossary:
+
+Glossary
+--------
+
+A glossary defines terms used throughout the documentation. References to glossary terms appear as follows.
+
+:term:`request`
+
+Note it is hyperlinked, and when clicked it will take the user to the term in the Glossary and highlight the term.
+
+
+.. _typographical-conventions-links:
+
+Links
+-----
+
+Links are presented as follows, and may be clickable.
+
+`TryPyramid <https://TryPyramid.com>`_
+
+.. seealso:: See also :ref:`typographical-conventions-cross-references` for other links within the documentation.
+
+
+.. _typographical-conventions-topic:
+
+Topic
+-----
+
+A topic is similar to a block quote with a title, or a self-contained section with no subsections. A topic indicates a self-contained idea that is separate from the flow of the document. Topics may occur anywhere a section or transition may occur.
+
+.. topic:: Topic Title
+
+ Subsequent indented lines comprise
+ the body of the topic, and are
+ interpreted as body elements.
+
+
+.. _typographical-conventions-displaying-code:
+
+Code
+----
+
+Code may be displayed in blocks or inline. Blocks of code may use syntax highlighting, line numbering, and emphasis.
+
+
+.. _typographical-conventions-syntax-highlighting:
+
+Syntax highlighting
+^^^^^^^^^^^^^^^^^^^
+
+XML:
+
+.. code-block:: xml
+
+ <somesnippet>Some XML</somesnippet>
+
+Unix shell commands are prefixed with a ``$`` character. (See :term:`venv` for the meaning of ``$VENV``.)
+
+.. code-block:: bash
+
+ $ $VENV/bin/pip install -e .
+
+Windows commands are prefixed with a drive letter with an optional directory name. (See :term:`venv` for the meaning of ``%VENV%``.)
+
+.. code-block:: doscon
+
+ c:\> %VENV%\Scripts\pcreate -s starter MyProject
+
+cfg:
+
+.. code-block:: cfg
+
+ [some-part]
+ # A random part in the buildout
+ recipe = collective.recipe.foo
+ option = value
+
+ini:
+
+.. code-block:: ini
+
+ [nosetests]
+ match=^test
+ where=pyramid
+ nocapture=1
+
+Interactive Python:
+
+.. code-block:: pycon
+
+ >>> class Foo:
+ ... bar = 100
+ ...
+ >>> f = Foo()
+ >>> f.bar
+ 100
+ >>> f.bar / 0
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ ZeroDivisionError: integer division or modulo by zero
+
+
+.. _typographical-conventions-long-commands:
+
+Displaying long commands
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+When a command that should be typed on one line is too long to fit on the displayed width of a page, the backslash character ``\`` is used to indicate that the subsequent printed line should be part of the command:
+
+.. code-block:: bash
+
+ $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \
+ --cov=tutorial -q
+
+
+.. _typographical-conventions-code-block-options:
+
+Code block options
+^^^^^^^^^^^^^^^^^^
+
+To emphasize lines, we give the appearance that a highlighting pen has been used on the code.
+
+.. code-block:: python
+ :emphasize-lines: 1,3
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+A code block with line numbers.
+
+.. code-block:: python
+ :linenos:
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+Some code blocks may be given a caption.
+
+.. code-block:: python
+ :caption: sample.py
+ :name: sample-py-typographical-conventions
+
+ if "foo" == "bar":
+ # This is Python code
+ pass
+
+
+.. _typographical-conventions-inline-code:
+
+Inline code
+^^^^^^^^^^^
+
+Inline code is displayed as follows, where the inline code is 'pip install -e ".[docs]"'.
+
+Install requirements for building documentation: ``pip install -e ".[docs]"``
+
+
+.. _typographical-conventions-feature-versioning:
+
+Feature versioning
+------------------
+
+We designate the version in which something is added, changed, or deprecated in the project.
+
+
+.. _typographical-conventions-version-added:
+
+Version added
+^^^^^^^^^^^^^
+
+The version in which a feature is added to a project is displayed as follows.
+
+.. versionadded:: 1.1
+ :func:`pyramid.paster.bootstrap`
+
+
+.. _typographical-conventions-version-changed:
+
+Version changed
+^^^^^^^^^^^^^^^
+
+The version in which a feature is changed in a project is displayed as follows.
+
+.. versionchanged:: 1.8
+ Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement.
+
+
+.. _typographical-conventions-deprecated:
+
+Deprecated
+^^^^^^^^^^
+
+The version in which a feature is deprecated in a project is displayed as follows.
+
+.. deprecated:: 1.7
+ Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised.
+
+
+.. _typographical-conventions-danger:
+
+Danger
+------
+
+Danger represents critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing".
+
+.. danger::
+
+ This is danger or an error.
+
+
+.. _typographical-conventions-warnings:
+
+Warnings
+--------
+
+Warnings represent limitations and advice related to a topic or concept.
+
+.. warning::
+
+ This is a warning.
+
+
+.. _typographical-conventions-notes:
+
+Notes
+-----
+
+Notes represent additional information related to a topic or concept.
+
+.. note::
+
+ This is a note.
+
+
+.. _typographical-conventions-see-also:
+
+See also
+--------
+
+"See also" messages refer to topics that are related to the current topic, but have a narrative tone to them instead of merely a link without explanation. "See also" is rendered in a block as well, so that it stands out for the reader's attention.
+
+.. seealso::
+
+ See :ref:`Quick Tutorial section on Requirements <qtut_requirements>`.
+
+
+.. _typographical-conventions-todo:
+
+Todo
+----
+
+Todo items designated tasks that require further work.
+
+.. todo::
+
+ This is a todo item.
+
+
+.. _typographical-conventions-cross-references:
+
+Cross-references
+----------------
+
+Cross-references are links that may be to a document, arbitrary location, object, or other items.
+
+
+.. _typographical-conventions-cross-referencing-documents:
+
+Cross-referencing documents
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Links to pages within this documentation display as follows.
+
+:doc:`quick_tour`
+
+
+.. _typographical-conventions-cross-referencing-arbitrary-locations:
+
+Cross-referencing arbitrary locations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Links to sections, and tables and figures with captions, within this documentation display as follows.
+
+:ref:`i18n_chapter`
+
+
+.. _typographical-conventions-cross-referencing-python:
+
+Python modules, classes, methods, and functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+All of the following are clickable links to Python modules, classes, methods, and functions.
+
+Python module names display as follows.
+
+:mod:`pyramid.config`
+
+Python class names display as follows.
+
+:class:`pyramid.config.Configurator`
+
+Python method names display as follows.
+
+:meth:`pyramid.config.Configurator.add_view`
+
+Python function names display as follows.
+
+:func:`pyramid.renderers.render_to_response`
+
+Sometimes we show only the last segment of a Python object's name, which displays as follows.
+
+:func:`~pyramid.renderers.render_to_response`
+
+The application "Pyramid" itself displays as follows.
+
+:app:`Pyramid`
+
diff --git a/docs/whatsnew-1.7.rst b/docs/whatsnew-1.7.rst
index 398b12f01..c5f611f04 100644
--- a/docs/whatsnew-1.7.rst
+++ b/docs/whatsnew-1.7.rst
@@ -126,7 +126,7 @@ Feature Additions
- The :attr:`pyramid.tweens.EXCVIEW` tween will now re-raise the original
exception if no exception view could be found to handle it. This allows
- the exception to be handled upstream by another tween or middelware.
+ the exception to be handled upstream by another tween or middleware.
See https://github.com/Pylons/pyramid/pull/2567
Deprecations
diff --git a/pyramid/asset.py b/pyramid/asset.py
index e6a145341..9d7a3ee63 100644
--- a/pyramid/asset.py
+++ b/pyramid/asset.py
@@ -33,7 +33,7 @@ def asset_spec_from_abspath(abspath, package):
relpath.replace(os.path.sep, '/'))
return abspath
-# bw compat only; use pyramid.path.AssetDescriptor.abspath() instead
+# bw compat only; use pyramid.path.AssetResolver().resolve(spec).abspath()
def abspath_from_asset_spec(spec, pname='__main__'):
if pname is None:
return spec
diff --git a/pyramid/config/predicates.py b/pyramid/config/predicates.py
index 0b76bbd70..5d99d6564 100644
--- a/pyramid/config/predicates.py
+++ b/pyramid/config/predicates.py
@@ -1,301 +1,2 @@
-import re
-
-from pyramid.exceptions import ConfigurationError
-
-from pyramid.compat import is_nonstr_iter
-
-from pyramid.traversal import (
- find_interface,
- traversal_path,
- resource_path_tuple
- )
-
-from pyramid.urldispatch import _compile_route
-from pyramid.util import object_description
-from pyramid.session import check_csrf_token
-
-from .util import as_sorted_tuple
-
-_marker = object()
-
-class XHRPredicate(object):
- def __init__(self, val, config):
- self.val = bool(val)
-
- def text(self):
- return 'xhr = %s' % self.val
-
- phash = text
-
- def __call__(self, context, request):
- return bool(request.is_xhr) is self.val
-
-class RequestMethodPredicate(object):
- def __init__(self, val, config):
- request_method = as_sorted_tuple(val)
- if 'GET' in request_method and 'HEAD' not in request_method:
- # GET implies HEAD too
- request_method = as_sorted_tuple(request_method + ('HEAD',))
- self.val = request_method
-
- def text(self):
- return 'request_method = %s' % (','.join(self.val))
-
- phash = text
-
- def __call__(self, context, request):
- return request.method in self.val
-
-class PathInfoPredicate(object):
- def __init__(self, val, config):
- self.orig = val
- try:
- val = re.compile(val)
- except re.error as why:
- raise ConfigurationError(why.args[0])
- self.val = val
-
- def text(self):
- return 'path_info = %s' % (self.orig,)
-
- phash = text
-
- def __call__(self, context, request):
- return self.val.match(request.upath_info) is not None
-
-class RequestParamPredicate(object):
- def __init__(self, val, config):
- val = as_sorted_tuple(val)
- reqs = []
- for p in val:
- k = p
- v = None
- if p.startswith('='):
- if '=' in p[1:]:
- k, v = p[1:].split('=', 1)
- k = '=' + k
- k, v = k.strip(), v.strip()
- elif '=' in p:
- k, v = p.split('=', 1)
- k, v = k.strip(), v.strip()
- reqs.append((k, v))
- self.val = val
- self.reqs = reqs
-
- def text(self):
- return 'request_param %s' % ','.join(
- ['%s=%s' % (x,y) if y else x for x, y in self.reqs]
- )
-
- phash = text
-
- def __call__(self, context, request):
- for k, v in self.reqs:
- actual = request.params.get(k)
- if actual is None:
- return False
- if v is not None and actual != v:
- return False
- return True
-
-class HeaderPredicate(object):
- def __init__(self, val, config):
- name = val
- v = None
- if ':' in name:
- name, val_str = name.split(':', 1)
- try:
- v = re.compile(val_str)
- except re.error as why:
- raise ConfigurationError(why.args[0])
- if v is None:
- self._text = 'header %s' % (name,)
- else:
- self._text = 'header %s=%s' % (name, val_str)
- self.name = name
- self.val = v
-
- def text(self):
- return self._text
-
- phash = text
-
- def __call__(self, context, request):
- if self.val is None:
- return self.name in request.headers
- val = request.headers.get(self.name)
- if val is None:
- return False
- return self.val.match(val) is not None
-
-class AcceptPredicate(object):
- def __init__(self, val, config):
- self.val = val
-
- def text(self):
- return 'accept = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- return self.val in request.accept
-
-class ContainmentPredicate(object):
- def __init__(self, val, config):
- self.val = config.maybe_dotted(val)
-
- def text(self):
- return 'containment = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- ctx = getattr(request, 'context', context)
- return find_interface(ctx, self.val) is not None
-
-class RequestTypePredicate(object):
- def __init__(self, val, config):
- self.val = val
-
- def text(self):
- return 'request_type = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- return self.val.providedBy(request)
-
-class MatchParamPredicate(object):
- def __init__(self, val, config):
- val = as_sorted_tuple(val)
- self.val = val
- reqs = [ p.split('=', 1) for p in val ]
- self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
-
- def text(self):
- return 'match_param %s' % ','.join(
- ['%s=%s' % (x,y) for x, y in self.reqs]
- )
-
- phash = text
-
- def __call__(self, context, request):
- if not request.matchdict:
- # might be None
- return False
- for k, v in self.reqs:
- if request.matchdict.get(k) != v:
- return False
- return True
-
-class CustomPredicate(object):
- def __init__(self, func, config):
- self.func = func
-
- def text(self):
- return getattr(
- self.func,
- '__text__',
- 'custom predicate: %s' % object_description(self.func)
- )
-
- def phash(self):
- # using hash() here rather than id() is intentional: we
- # want to allow custom predicates that are part of
- # frameworks to be able to define custom __hash__
- # functions for custom predicates, so that the hash output
- # of predicate instances which are "logically the same"
- # may compare equal.
- return 'custom:%r' % hash(self.func)
-
- def __call__(self, context, request):
- return self.func(context, request)
-
-
-class TraversePredicate(object):
- # Can only be used as a *route* "predicate"; it adds 'traverse' to the
- # matchdict if it's specified in the routing args. This causes the
- # ResourceTreeTraverser to use the resolved traverse pattern as the
- # traversal path.
- def __init__(self, val, config):
- _, self.tgenerate = _compile_route(val)
- self.val = val
-
- def text(self):
- return 'traverse matchdict pseudo-predicate'
-
- def phash(self):
- # This isn't actually a predicate, it's just a infodict modifier that
- # injects ``traverse`` into the matchdict. As a result, we don't
- # need to update the hash.
- return ''
-
- def __call__(self, context, request):
- if 'traverse' in context:
- return True
- m = context['match']
- tvalue = self.tgenerate(m) # tvalue will be urlquoted string
- m['traverse'] = traversal_path(tvalue)
- # This isn't actually a predicate, it's just a infodict modifier that
- # injects ``traverse`` into the matchdict. As a result, we just
- # return True.
- return True
-
-class CheckCSRFTokenPredicate(object):
-
- check_csrf_token = staticmethod(check_csrf_token) # testing
-
- def __init__(self, val, config):
- self.val = val
-
- def text(self):
- return 'check_csrf = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- val = self.val
- if val:
- if val is True:
- val = 'csrf_token'
- return self.check_csrf_token(request, val, raises=False)
- return True
-
-class PhysicalPathPredicate(object):
- def __init__(self, val, config):
- if is_nonstr_iter(val):
- self.val = tuple(val)
- else:
- val = tuple(filter(None, val.split('/')))
- self.val = ('',) + val
-
- def text(self):
- return 'physical_path = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- if getattr(context, '__name__', _marker) is not _marker:
- return resource_path_tuple(context) == self.val
- return False
-
-class EffectivePrincipalsPredicate(object):
- def __init__(self, val, config):
- if is_nonstr_iter(val):
- self.val = set(val)
- else:
- self.val = set((val,))
-
- def text(self):
- return 'effective_principals = %s' % sorted(list(self.val))
-
- phash = text
-
- def __call__(self, context, request):
- req_principals = request.effective_principals
- if is_nonstr_iter(req_principals):
- rpset = set(req_principals)
- if self.val.issubset(rpset):
- return True
- return False
-
+import zope.deprecation
+zope.deprecation.moved('pyramid.predicates', 'Pyramid 2.0')
diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py
index 90d4d47d2..203baa128 100644
--- a/pyramid/config/routes.py
+++ b/pyramid/config/routes.py
@@ -13,12 +13,10 @@ from pyramid.registry import predvalseq
from pyramid.request import route_request_iface
from pyramid.urldispatch import RoutesMapper
-from pyramid.config.util import (
- action_method,
- as_sorted_tuple,
- )
+from pyramid.config.util import action_method
+from pyramid.util import as_sorted_tuple
-import pyramid.config.predicates
+import pyramid.predicates
class RoutesConfiguratorMixin(object):
@action_method
@@ -446,7 +444,7 @@ class RoutesConfiguratorMixin(object):
)
def add_default_route_predicates(self):
- p = pyramid.config.predicates
+ p = pyramid.predicates
for (name, factory) in (
('xhr', p.XHRPredicate),
('request_method', p.RequestMethodPredicate),
diff --git a/pyramid/config/security.py b/pyramid/config/security.py
index 02732c042..33593376b 100644
--- a/pyramid/config/security.py
+++ b/pyramid/config/security.py
@@ -9,9 +9,9 @@ from pyramid.interfaces import (
PHASE2_CONFIG,
)
-from pyramid.config.util import as_sorted_tuple
from pyramid.exceptions import ConfigurationError
from pyramid.util import action_method
+from pyramid.util import as_sorted_tuple
class SecurityConfiguratorMixin(object):
@action_method
diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py
index 0aeb01fe3..16712ab16 100644
--- a/pyramid/config/tweens.py
+++ b/pyramid/config/tweens.py
@@ -17,9 +17,9 @@ from pyramid.tweens import (
from pyramid.config.util import (
action_method,
- is_string_or_iterable,
TopologicalSorter,
)
+from pyramid.util import is_string_or_iterable
class TweensConfiguratorMixin(object):
def add_tween(self, tween_factory, under=None, over=None):
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
index 626e8d5fe..67bba9593 100644
--- a/pyramid/config/util.py
+++ b/pyramid/config/util.py
@@ -4,8 +4,7 @@ import inspect
from pyramid.compat import (
bytes_,
getargspec,
- is_nonstr_iter,
- string_types,
+ is_nonstr_iter
)
from pyramid.compat import im_func
@@ -24,18 +23,6 @@ ActionInfo = ActionInfo # support bw compat imports
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
-def is_string_or_iterable(v):
- if isinstance(v, string_types):
- return True
- if hasattr(v, '__iter__'):
- return True
-
-def as_sorted_tuple(val):
- if not is_nonstr_iter(val):
- val = (val,)
- val = tuple(sorted(val))
- return val
-
class not_(object):
"""
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 6082d8b48..65c9da585 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -70,10 +70,11 @@ import pyramid.util
from pyramid.util import (
viewdefaults,
action_method,
+ as_sorted_tuple,
TopologicalSorter,
)
-import pyramid.config.predicates
+import pyramid.predicates
import pyramid.viewderivers
from pyramid.viewderivers import (
@@ -89,7 +90,6 @@ from pyramid.viewderivers import (
from pyramid.config.util import (
DEFAULT_PHASH,
MAX_ORDER,
- as_sorted_tuple,
)
urljoin = urlparse.urljoin
@@ -1143,7 +1143,7 @@ class ViewsConfiguratorMixin(object):
)
def add_default_view_predicates(self):
- p = pyramid.config.predicates
+ p = pyramid.predicates
for (name, factory) in (
('xhr', p.XHRPredicate),
('request_method', p.RequestMethodPredicate),
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index 054917dfa..a22b088c6 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -246,7 +246,7 @@ ${body}''')
'title': self.title}
def prepare(self, environ):
- if not self.body and not self.empty_body:
+ if not self.has_body and not self.empty_body:
html_comment = ''
comment = self.comment or ''
accept_value = environ.get('HTTP_ACCEPT', '')
diff --git a/pyramid/predicates.py b/pyramid/predicates.py
new file mode 100644
index 000000000..7c3a778ca
--- /dev/null
+++ b/pyramid/predicates.py
@@ -0,0 +1,300 @@
+import re
+
+from pyramid.exceptions import ConfigurationError
+
+from pyramid.compat import is_nonstr_iter
+
+from pyramid.session import check_csrf_token
+from pyramid.traversal import (
+ find_interface,
+ traversal_path,
+ resource_path_tuple
+ )
+
+from pyramid.urldispatch import _compile_route
+from pyramid.util import object_description
+from pyramid.util import as_sorted_tuple
+
+_marker = object()
+
+class XHRPredicate(object):
+ def __init__(self, val, config):
+ self.val = bool(val)
+
+ def text(self):
+ return 'xhr = %s' % self.val
+
+ phash = text
+
+ def __call__(self, context, request):
+ return bool(request.is_xhr) is self.val
+
+class RequestMethodPredicate(object):
+ def __init__(self, val, config):
+ request_method = as_sorted_tuple(val)
+ if 'GET' in request_method and 'HEAD' not in request_method:
+ # GET implies HEAD too
+ request_method = as_sorted_tuple(request_method + ('HEAD',))
+ self.val = request_method
+
+ def text(self):
+ return 'request_method = %s' % (','.join(self.val))
+
+ phash = text
+
+ def __call__(self, context, request):
+ return request.method in self.val
+
+class PathInfoPredicate(object):
+ def __init__(self, val, config):
+ self.orig = val
+ try:
+ val = re.compile(val)
+ except re.error as why:
+ raise ConfigurationError(why.args[0])
+ self.val = val
+
+ def text(self):
+ return 'path_info = %s' % (self.orig,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return self.val.match(request.upath_info) is not None
+
+class RequestParamPredicate(object):
+ def __init__(self, val, config):
+ val = as_sorted_tuple(val)
+ reqs = []
+ for p in val:
+ k = p
+ v = None
+ if p.startswith('='):
+ if '=' in p[1:]:
+ k, v = p[1:].split('=', 1)
+ k = '=' + k
+ k, v = k.strip(), v.strip()
+ elif '=' in p:
+ k, v = p.split('=', 1)
+ k, v = k.strip(), v.strip()
+ reqs.append((k, v))
+ self.val = val
+ self.reqs = reqs
+
+ def text(self):
+ return 'request_param %s' % ','.join(
+ ['%s=%s' % (x,y) if y else x for x, y in self.reqs]
+ )
+
+ phash = text
+
+ def __call__(self, context, request):
+ for k, v in self.reqs:
+ actual = request.params.get(k)
+ if actual is None:
+ return False
+ if v is not None and actual != v:
+ return False
+ return True
+
+class HeaderPredicate(object):
+ def __init__(self, val, config):
+ name = val
+ v = None
+ if ':' in name:
+ name, val_str = name.split(':', 1)
+ try:
+ v = re.compile(val_str)
+ except re.error as why:
+ raise ConfigurationError(why.args[0])
+ if v is None:
+ self._text = 'header %s' % (name,)
+ else:
+ self._text = 'header %s=%s' % (name, val_str)
+ self.name = name
+ self.val = v
+
+ def text(self):
+ return self._text
+
+ phash = text
+
+ def __call__(self, context, request):
+ if self.val is None:
+ return self.name in request.headers
+ val = request.headers.get(self.name)
+ if val is None:
+ return False
+ return self.val.match(val) is not None
+
+class AcceptPredicate(object):
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'accept = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return self.val in request.accept
+
+class ContainmentPredicate(object):
+ def __init__(self, val, config):
+ self.val = config.maybe_dotted(val)
+
+ def text(self):
+ return 'containment = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ ctx = getattr(request, 'context', context)
+ return find_interface(ctx, self.val) is not None
+
+class RequestTypePredicate(object):
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'request_type = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return self.val.providedBy(request)
+
+class MatchParamPredicate(object):
+ def __init__(self, val, config):
+ val = as_sorted_tuple(val)
+ self.val = val
+ reqs = [ p.split('=', 1) for p in val ]
+ self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
+
+ def text(self):
+ return 'match_param %s' % ','.join(
+ ['%s=%s' % (x,y) for x, y in self.reqs]
+ )
+
+ phash = text
+
+ def __call__(self, context, request):
+ if not request.matchdict:
+ # might be None
+ return False
+ for k, v in self.reqs:
+ if request.matchdict.get(k) != v:
+ return False
+ return True
+
+class CustomPredicate(object):
+ def __init__(self, func, config):
+ self.func = func
+
+ def text(self):
+ return getattr(
+ self.func,
+ '__text__',
+ 'custom predicate: %s' % object_description(self.func)
+ )
+
+ def phash(self):
+ # using hash() here rather than id() is intentional: we
+ # want to allow custom predicates that are part of
+ # frameworks to be able to define custom __hash__
+ # functions for custom predicates, so that the hash output
+ # of predicate instances which are "logically the same"
+ # may compare equal.
+ return 'custom:%r' % hash(self.func)
+
+ def __call__(self, context, request):
+ return self.func(context, request)
+
+
+class TraversePredicate(object):
+ # Can only be used as a *route* "predicate"; it adds 'traverse' to the
+ # matchdict if it's specified in the routing args. This causes the
+ # ResourceTreeTraverser to use the resolved traverse pattern as the
+ # traversal path.
+ def __init__(self, val, config):
+ _, self.tgenerate = _compile_route(val)
+ self.val = val
+
+ def text(self):
+ return 'traverse matchdict pseudo-predicate'
+
+ def phash(self):
+ # This isn't actually a predicate, it's just a infodict modifier that
+ # injects ``traverse`` into the matchdict. As a result, we don't
+ # need to update the hash.
+ return ''
+
+ def __call__(self, context, request):
+ if 'traverse' in context:
+ return True
+ m = context['match']
+ tvalue = self.tgenerate(m) # tvalue will be urlquoted string
+ m['traverse'] = traversal_path(tvalue)
+ # This isn't actually a predicate, it's just a infodict modifier that
+ # injects ``traverse`` into the matchdict. As a result, we just
+ # return True.
+ return True
+
+class CheckCSRFTokenPredicate(object):
+
+ check_csrf_token = staticmethod(check_csrf_token) # testing
+
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'check_csrf = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ val = self.val
+ if val:
+ if val is True:
+ val = 'csrf_token'
+ return self.check_csrf_token(request, val, raises=False)
+ return True
+
+class PhysicalPathPredicate(object):
+ def __init__(self, val, config):
+ if is_nonstr_iter(val):
+ self.val = tuple(val)
+ else:
+ val = tuple(filter(None, val.split('/')))
+ self.val = ('',) + val
+
+ def text(self):
+ return 'physical_path = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ if getattr(context, '__name__', _marker) is not _marker:
+ return resource_path_tuple(context) == self.val
+ return False
+
+class EffectivePrincipalsPredicate(object):
+ def __init__(self, val, config):
+ if is_nonstr_iter(val):
+ self.val = set(val)
+ else:
+ self.val = set((val,))
+
+ def text(self):
+ return 'effective_principals = %s' % sorted(list(self.val))
+
+ phash = text
+
+ def __call__(self, context, request):
+ req_principals = request.effective_principals
+ if is_nonstr_iter(req_principals):
+ rpset = set(req_principals)
+ if self.val.issubset(rpset):
+ return True
+ return False
+
diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py
index ccf7fa260..398b6fba8 100644
--- a/pyramid/tests/test_config/test_util.py
+++ b/pyramid/tests/test_config/test_util.py
@@ -5,7 +5,7 @@ class TestPredicateList(unittest.TestCase):
def _makeOne(self):
from pyramid.config.util import PredicateList
- from pyramid.config import predicates
+ from pyramid import predicates
inst = PredicateList()
for name, factory in (
('xhr', predicates.XHRPredicate),
@@ -594,6 +594,15 @@ class TestNotted(unittest.TestCase):
self.assertEqual(inst.phash(), '')
self.assertEqual(inst(None, None), True)
+
+class TestDeprecatedPredicates(unittest.TestCase):
+ def test_it(self):
+ import warnings
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always')
+ from pyramid.config.predicates import XHRPredicate
+ self.assertEqual(len(w), 1)
+
class DummyPredicate(object):
def __init__(self, result):
self.result = result
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index f020485de..211632730 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -2309,9 +2309,9 @@ class TestViewsConfigurationMixin(unittest.TestCase):
# Since Python 3 has to be all cool and fancy and different...
def _assertBody(self, response, value):
from pyramid.compat import text_type
- if isinstance(value, text_type): # pragma: nocover
+ if isinstance(value, text_type): # pragma: no cover
self.assertEqual(response.text, value)
- else: # pragma: nocover
+ else: # pragma: no cover
self.assertEqual(response.body, value)
def test_add_notfound_view_with_renderer(self):
diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py
index 6c6e16d55..224fa4cf0 100644
--- a/pyramid/tests/test_httpexceptions.py
+++ b/pyramid/tests/test_httpexceptions.py
@@ -348,7 +348,7 @@ class TestHTTPException(unittest.TestCase):
exc = cls(body_template='${REQUEST_METHOD}')
environ = _makeEnviron()
class Choke(object):
- def __str__(self): # pragma nocover
+ def __str__(self): # pragma no cover
raise ValueError
environ['gardentheory.user'] = Choke()
start_response = DummyStartResponse()
diff --git a/pyramid/tests/test_config/test_predicates.py b/pyramid/tests/test_predicates.py
index 9cd8f2734..8a002c24e 100644
--- a/pyramid/tests/test_config/test_predicates.py
+++ b/pyramid/tests/test_predicates.py
@@ -6,7 +6,7 @@ from pyramid.compat import text_
class TestXHRPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import XHRPredicate
+ from pyramid.predicates import XHRPredicate
return XHRPredicate(val, None)
def test___call___true(self):
@@ -33,7 +33,7 @@ class TestXHRPredicate(unittest.TestCase):
class TestRequestMethodPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import RequestMethodPredicate
+ from pyramid.predicates import RequestMethodPredicate
return RequestMethodPredicate(val, None)
def test_ctor_get_but_no_head(self):
@@ -71,7 +71,7 @@ class TestRequestMethodPredicate(unittest.TestCase):
class TestPathInfoPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import PathInfoPredicate
+ from pyramid.predicates import PathInfoPredicate
return PathInfoPredicate(val, None)
def test_ctor_compilefail(self):
@@ -102,7 +102,7 @@ class TestPathInfoPredicate(unittest.TestCase):
class TestRequestParamPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import RequestParamPredicate
+ from pyramid.predicates import RequestParamPredicate
return RequestParamPredicate(val, None)
def test___call___true_exists(self):
@@ -174,7 +174,7 @@ class TestRequestParamPredicate(unittest.TestCase):
class TestMatchParamPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import MatchParamPredicate
+ from pyramid.predicates import MatchParamPredicate
return MatchParamPredicate(val, None)
def test___call___true_single(self):
@@ -216,7 +216,7 @@ class TestMatchParamPredicate(unittest.TestCase):
class TestCustomPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import CustomPredicate
+ from pyramid.predicates import CustomPredicate
return CustomPredicate(val, None)
def test___call___true(self):
@@ -255,7 +255,7 @@ class TestCustomPredicate(unittest.TestCase):
class TestTraversePredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import TraversePredicate
+ from pyramid.predicates import TraversePredicate
return TraversePredicate(val, None)
def test___call__traverse_has_remainder_already(self):
@@ -297,7 +297,7 @@ class TestTraversePredicate(unittest.TestCase):
class Test_CheckCSRFTokenPredicate(unittest.TestCase):
def _makeOne(self, val, config):
- from pyramid.config.predicates import CheckCSRFTokenPredicate
+ from pyramid.predicates import CheckCSRFTokenPredicate
return CheckCSRFTokenPredicate(val, config)
def test_text(self):
@@ -340,7 +340,7 @@ class Test_CheckCSRFTokenPredicate(unittest.TestCase):
class TestHeaderPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import HeaderPredicate
+ from pyramid.predicates import HeaderPredicate
return HeaderPredicate(val, None)
def test___call___true_exists(self):
@@ -404,7 +404,7 @@ class TestHeaderPredicate(unittest.TestCase):
class Test_PhysicalPathPredicate(unittest.TestCase):
def _makeOne(self, val, config):
- from pyramid.config.predicates import PhysicalPathPredicate
+ from pyramid.predicates import PhysicalPathPredicate
return PhysicalPathPredicate(val, config)
def test_text(self):
@@ -468,7 +468,7 @@ class Test_EffectivePrincipalsPredicate(unittest.TestCase):
testing.tearDown()
def _makeOne(self, val, config):
- from pyramid.config.predicates import EffectivePrincipalsPredicate
+ from pyramid.predicates import EffectivePrincipalsPredicate
return EffectivePrincipalsPredicate(val, config)
def test_text(self):
diff --git a/pyramid/util.py b/pyramid/util.py
index 4936dcb24..3337d410d 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -3,7 +3,7 @@ import functools
try:
# py2.7.7+ and py3.3+ have native comparison support
from hmac import compare_digest
-except ImportError: # pragma: nocover
+except ImportError: # pragma: no cover
compare_digest = None
import inspect
import traceback
@@ -28,13 +28,24 @@ from pyramid.compat import (
from pyramid.interfaces import IActionInfo
from pyramid.path import DottedNameResolver as _DottedNameResolver
+_marker = object()
+
class DottedNameResolver(_DottedNameResolver):
def __init__(self, package=None): # default to package = None for bw compat
_DottedNameResolver.__init__(self, package)
-_marker = object()
-
+def is_string_or_iterable(v):
+ if isinstance(v, string_types):
+ return True
+ if hasattr(v, '__iter__'):
+ return True
+
+def as_sorted_tuple(val):
+ if not is_nonstr_iter(val):
+ val = (val,)
+ val = tuple(sorted(val))
+ return val
class InstancePropertyHelper(object):
"""A helper object for assigning properties and descriptors to instances.
diff --git a/setup.py b/setup.py
index 36615f36b..383991d73 100644
--- a/setup.py
+++ b/setup.py
@@ -14,19 +14,21 @@
import os
import sys
+import warnings
from setuptools import setup, find_packages
py_version = sys.version_info[:2]
-PY3 = py_version[0] == 3
+PY2 = py_version[0] == 2
-if PY3:
- if py_version < (3, 4):
- raise RuntimeError('On Python 3, Pyramid requires Python 3.4 or better')
-else:
- if py_version < (2, 7):
- raise RuntimeError('On Python 2, Pyramid requires Python 2.7 or better')
+if (3, 0) <= py_version < (3, 4):
+ warnings.warn(
+ 'On Python 3, Pyramid only supports Python 3.4 or better',
+ UserWarning,
+ )
+elif py_version < (2, 7):
+ raise RuntimeError('On Python 2, Pyramid requires Python 2.7 or better')
here = os.path.abspath(os.path.dirname(__file__))
try:
@@ -39,7 +41,7 @@ except IOError:
install_requires = [
'setuptools',
- 'WebOb >= 1.3.1', # request.domain and CookieProfile
+ 'WebOb >= 1.7.0rc2', # Response.has_body
'repoze.lru >= 0.4', # py3 compat
'zope.interface >= 3.8.0', # has zope.interface.registry
'zope.deprecation >= 3.5.0', # py3 compat
@@ -53,7 +55,7 @@ tests_require = [
'WebTest >= 1.3.1', # py3 compat
]
-if not PY3:
+if PY2:
tests_require.append('zope.component>=3.11.0')
docs_extras = [
@@ -83,6 +85,7 @@ setup(name='pyramid',
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Framework :: Pyramid",