Michael Merickel
2018-10-16 8eed333343e4e9e7f11f3aee67299030d6bf2783
docs/quick_tour.rst
@@ -10,23 +10,23 @@
frameworks, or a pro in a hurry. For more detailed treatment of each topic,
give the :ref:`quick_tutorial` a try.
If you would prefer to cut and paste the example code in this tour you may browse the source code located in the `Pyramid repository in the directory "docs/quick_tour" <https://github.com/Pylons/pyramid/>`. If you have downloaded the source code, you will find the tour in the same location.
Installation
============
Once you have a standard Python environment setup, getting started with Pyramid
is a breeze. Unfortunately "standard" is not so simple in Python. For this
Quick Tour, it means `Python <https://www.python.org/downloads/>`_, `venv
<https://packaging.python.org/en/latest/projects/#venv>`_ (or `virtualenv for
Python 2.7 <https://packaging.python.org/en/latest/projects/#virtualenv>`_),
`pip <https://packaging.python.org/en/latest/projects/#pip>`_, and `setuptools
<https://packaging.python.org/en/latest/projects/#easy-install>`_.
Quick Tour, it means `Python <https://www.python.org/downloads/>`_, :mod:`python:venv` (or `virtualenv for
Python 2.7 <https://virtualenv.pypa.io/en/stable/>`_),
`pip <https://pypi.org/project/pip/>`_, and `Setuptools
<https://pypi.org/project/setuptools/>`_.
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.6+ on Linux:
As an example, for Python 3 on Linux:
.. parsed-literal::
@@ -46,7 +46,7 @@
    # set an environment variable to where you want your virtual environment
    c:\\> set VENV=c:\\env
    # create the virtual environment
    c:\\> %VENV%\\Scripts\\python -m venv %VENV%
    c:\\> python -m venv %VENV%
    # install pyramid
    c:\\> %VENV%\\Scripts\\pip install pyramid
    # or for a specific released version
@@ -55,7 +55,7 @@
Of course Pyramid runs fine on Python 2.7+, as do the examples in this *Quick
Tour*. We're showing Python 3 for simplicity. (Pyramid had production support
for Python 3 in October 2011.) Also for simplicity, the remaining examples will
show only UNIX commands.
show only Unix commands.
.. seealso:: See also:
    :ref:`Quick Tutorial section on Requirements <qtut_requirements>`,
@@ -87,13 +87,13 @@
New to Python web programming? If so, some lines in the module merit
explanation:
#. *Lines 6-7*. Implement the view code that generates the :term:`response`.
#. *Line 10*. ``if __name__ == '__main__':`` is Python's way of saying "Start
   here when running from the command line".
#. *Lines 11-13*. Use Pyramid's :term:`configurator` to connect :term:`view`
#. *Lines 11-13*. Use Pyramid's :term:`configurator` in a :term:`context manager` to connect :term:`view`
   code to a particular URL :term:`route`.
#. *Lines 6-7*. Implement the view code that generates the :term:`response`.
#. *Lines 14-16*. Publish a :term:`WSGI` app using an HTTP server.
@@ -116,7 +116,7 @@
for web requests.
Pyramid has always fit nicely into the existing world of Python web development
(virtual environments, packaging, scaffolding, one of the first to embrace
(virtual environments, packaging, cookiecutters, one of the first to embrace
Python 3, etc.). Pyramid turned to the well-regarded :term:`WebOb` Python
library for request and response handling. In our example above, Pyramid hands
``hello_world`` a ``request`` that is :ref:`based on WebOb <webob_chapter>`.
@@ -133,7 +133,7 @@
.. code-block:: text
  URL http://localhost:6543/?name=alice with name: alice
    URL http://localhost:6543/?name=alice with name: alice
Finally we set the response's content type, and return the Response.
@@ -263,7 +263,7 @@
.. code-block:: bash
    $ $VENV/bin/pip install pyramid_chameleon
    $VENV/bin/pip install pyramid_chameleon
With the package installed, we can include the template bindings into our
configuration in ``app.py``:
@@ -309,7 +309,7 @@
.. code-block:: bash
    $ $VENV/bin/pip install pyramid_jinja2
    $VENV/bin/pip install pyramid_jinja2
With the package installed, we can include the template bindings into our
configuration:
@@ -498,41 +498,53 @@
packages, no structure. Most Pyramid projects, though, aren't developed this
way.
To ease the process of getting started, the Pylons Project provides :term:`cookiecutter`\ s that generate sample Pyramid projects from project templates. In addition these cookiecutters will install Pyramid and its dependencies.
To ease the process of getting started, the Pylons Project provides a :term:`cookiecutter` that generates sample Pyramid projects from project templates. This cookiecutter will install Pyramid and its dependencies as well.
First you'll need to install cookiecutter.
.. code-block:: bash
    $ $VENV/bin/pip install cookiecutter
    $VENV/bin/pip install cookiecutter
In the directory where we want our sample Pyramid project to be generated, let's use the cookiecutter ``pyramid-cookiecutter-starter``, following the prompts of the command.
Let's use the cookiecutter ``pyramid-cookiecutter-starter`` to create a starter Pyramid project in the current directory, entering values at the prompts as shown below for the following command.
.. code-block:: bash
    $ $VENV/bin/cookiecutter https://github.com/Pylons/pyramid-cookiecutter-starter
    $VENV/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout 1.10-branch
If prompted for the first item, accept the default ``yes`` by hitting return.
#. ``You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before. Is it
   okay to delete and re-clone it? [yes]:``
#. ``project_name [Pyramid Scaffold]: myproject``
#. ``repo_name [scaffold]: qtour``
.. code-block:: text
We then run through the following commands
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: hello_world
    repo_name [hello_world]: hello_world
    Select template_language:
    1 - jinja2
    2 - chameleon
    3 - mako
    Choose from 1, 2, 3 [1]: 1
    Select backend:
    1 - none
    2 - sqlalchemy
    3 - zodb
    Choose from 1, 2, 3 [1]: 1
We then run through the following commands.
.. code-block:: bash
    # Change directory into your newly created project.
    $ cd qtour
    cd hello_world
    # Create a new virtual environment...
    $ python3 -m venv env
    python3 -m venv env
    # ...where we upgrade packaging tools...
    $ env/bin/pip install --upgrade pip setuptools
    env/bin/pip install --upgrade pip setuptools
    # ...and into which we install our project and its testing requirements.
    $ env/bin/pip install -e ".[testing]"
    # Reset our environment variable to use the new virtual environment.
    $ export VENV=~/env/qtour/env
    env/bin/pip install -e ".[testing]"
    # Reset our environment variable for a new virtual environment.
    export VENV=~/hello_world/env
We are moving in the direction of a full-featured Pyramid project, with a
proper setup for Python standards (packaging) and Pyramid configuration. This
@@ -540,19 +552,19 @@
.. code-block:: bash
    $ $VENV/bin/pserve development.ini
    $VENV/bin/pserve development.ini
Let's look at ``pserve`` and configuration in more depth.
.. seealso:: See also:
    :ref:`Quick Tutorial Cookiecutters <qtut_scaffolds>`,
    :ref:`Quick Tutorial Cookiecutters <qtut_cookiecutters>`,
    :ref:`project_narr`, and
    :doc:`../narr/scaffolding`
    :doc:`../narr/cookiecutters`
Application running with ``pserve``
===================================
Prior to scaffolds, our project mixed a number of operational details into our
Prior to the cookiecutter, our project mixed a number of operational details into our
code. Why should my main code care which HTTP server I want and what port
number to run on?
@@ -567,7 +579,7 @@
.. code-block:: bash
    $ $VENV/bin/pserve development.ini --reload
    $VENV/bin/pserve development.ini --reload
The ``pserve`` command has a number of other options and operations. Most of
the work, though, comes from your project's wiring, as expressed in the
@@ -582,7 +594,7 @@
Earlier in *Quick Tour* we first met Pyramid's configuration system. At that
point we did all configuration in Python code. For example, the port number
chosen for our HTTP server was right there in Python code. Our scaffold has
chosen for our HTTP server was right there in Python code. Our cookiecutter has
moved this decision and more into the ``development.ini`` file:
.. literalinclude:: quick_tour/package/development.ini
@@ -599,11 +611,6 @@
We have a few decisions made for us in this configuration:
#. *Choice of web server:* ``use = egg:hello_world`` tells ``pserve`` to
   use the ``waitress`` server.
#. *Port number:* ``port = 6543`` tells ``waitress`` to listen on port 6543.
#. *WSGI app:* What package has our WSGI application in it?
   ``use = egg:hello_world`` in the app section tells the configuration what
   application to load.
@@ -613,7 +620,12 @@
   ``pyramid.reload_templates = true`` sets this policy, which might be
   different in production.
Additionally the ``development.ini`` generated by this scaffold wired up
#. *Choice of web server:* ``use = egg:waitress#main`` tells ``pserve`` to
   use the ``waitress`` server.
#. *Interfaces:* ``listen = localhost:6543`` tells ``waitress`` to listen on all interfaces on port 6543 for both IPv4 and IPv6.
Additionally the ``development.ini`` generated by this cookiecutter wired up
Python's standard logging. We'll now see in the console, for example, a log on
every request that comes in, as well as traceback information.
@@ -634,38 +646,28 @@
available in your browser. Adding it to your project illustrates several points
about configuration.
The scaffold ``pyramid_jinja2_starter`` is already configured to include the
Our cookiecutter ``pyramid-cookiecutter-starter`` already configured our package to include the
add-on ``pyramid_debugtoolbar`` in its ``setup.py``:
.. literalinclude:: quick_tour/package/setup.py
    :language: python
    :linenos:
    :lineno-start: 11
    :lines: 11-16
    :lineno-match:
    :lines: 11-17
    :emphasize-lines: 5
It was installed when you previously ran:
.. code-block:: bash
    $ $VENV/bin/pip install -e .
    $VENV/bin/pip install -e ".[testing]"
The ``pyramid_debugtoolbar`` package is a Pyramid add-on, which means we need
to include its configuration into our web application. The ``pyramid_jinja2``
add-on already took care of this for us in its ``__init__.py``:
.. literalinclude:: quick_tour/package/hello_world/__init__.py
    :language: python
    :linenos:
    :lineno-start: 16
    :lines: 19
And it uses the ``pyramid.includes`` facility in our ``development.ini``:
to include its configuration into our web application. The cookiecutter already took care of this for us in its ``development.ini`` using the ``pyramid.includes`` facility:
.. literalinclude:: quick_tour/package/development.ini
    :language: ini
    :linenos:
    :lineno-start: 15
    :lines: 15-16
    :lineno-match:
    :lines: 14-15
You'll now see a Pyramid logo on the right side of your browser window, which
when clicked opens a new window that provides introspective access to debugging
@@ -678,87 +680,66 @@
    :ref:`Quick Tutorial pyramid_debugtoolbar <qtut_debugtoolbar>` and
    :ref:`pyramid_debugtoolbar <toolbar:overview>`
Unit tests and ``py.test``
==========================
Unit tests and ``pytest``
=========================
Yikes! We got this far and we haven't yet discussed tests. This is particularly
egregious, as Pyramid has had a deep commitment to full test coverage since
before its release.
Our ``pyramid_jinja2_starter`` scaffold generated a ``tests.py`` module with
one unit test in it. It also configured ``setup.py`` with test requirements:
``py.test`` as the test runner, ``WebTest`` for running view tests, and the
``pytest-cov`` tool which yells at us for code that isn't tested. The
highlighted lines show this:
Our ``pyramid-cookiecutter-starter`` cookiecutter generated a ``tests.py`` module with
one unit test and one functional test in it. It also configured ``setup.py`` with test requirements:
``pytest`` as the test runner, ``WebTest`` for running view tests, and the
``pytest-cov`` tool which yells at us for code that isn't tested:
.. code-block:: python
    :linenos:
    :lineno-start: 11
    :emphasize-lines: 8-12
.. literalinclude:: quick_tour/package/setup.py
    :language: python
    :lineno-match:
    :lines: 19-23
    requires = [
        'pyramid',
        'pyramid_jinja2',
        'pyramid_debugtoolbar',
        'waitress',
    ]
.. literalinclude:: quick_tour/package/setup.py
    :language: python
    :lineno-match:
    :lines: 43-45
    tests_require = [
        'WebTest >= 1.3.1',  # py3 compat
        'pytest',  # includes virtualenv
        'pytest-cov',
        ]
.. code-block:: python
    :linenos:
    :lineno-start: 34
    :emphasize-lines: 2-4
        zip_safe=False,
        extras_require={
          'testing': tests_require,
        },
To install the test requirements, run ``$VENV/bin/pip install -e
".[testing]"``. We can now run all our tests:
We already installed the test requirements when we ran the command ``$VENV/bin/pip install -e ".[testing]"``. We can now run all our tests:
.. code-block:: bash
    $ $VENV/bin/py.test --cov --cov-report=term-missing
    $VENV/bin/pytest --cov --cov-report=term-missing
This yields the following output.
.. code-block:: text
    =========================== test session starts ===========================
    platform darwin -- Python 3.5.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
    rootdir: /Users/stevepiercy/projects/hack-on-pyramid/hello_world, inifile:
    plugins: cov-2.2.1
    collected 1 items
    platform darwin -- Python 3.6.0, pytest-3.0.5, py-1.4.32, pluggy-0.4.0
    rootdir: /Users/stevepiercy/hello_world, inifile: pytest.ini
    plugins: cov-2.4.0
    collected 2 items
    hello_world/tests.py .
    hello_world/tests.py ..
    ------------- coverage: platform darwin, python 3.6.0-final-0 -------------
    Name                       Stmts   Miss  Cover   Missing
    --------------------------------------------------------
    hello_world/__init__.py       11      8    27%   11-23
    hello_world/resources.py       5      1    80%   8
    hello_world/tests.py          14      0   100%
    hello_world/views.py           4      0   100%
    --------------------------------------------------------
    TOTAL                         34      9    74%
    Name                                      Stmts   Miss  Cover   Missing
    -----------------------------------------------------------------------
    hello_world/__init__.py                       8      0   100%
    hello_world/views.py                          3      0   100%
    -----------------------------------------------------------------------
    TOTAL                                        11      0   100%
    ========================= 1 passed in 0.22 seconds =========================
Our unit test passed, although its coverage is incomplete. What did our test
look like?
    ========================= 2 passed in 1.37 seconds =========================
Our tests passed, and its coverage is complete. What did our test look like?
.. literalinclude:: quick_tour/package/hello_world/tests.py
    :language: python
    :linenos:
Pyramid supplies helpers for test writing, which we use in the test setup and
teardown. Our one test imports the view, makes a dummy request, and sees if the
view returns what we expected.
teardown. Our first test imports the view, makes a dummy request, and sees if the
view returns what we expected. Our second test verifies that the response body from a request to the web root contains what we expected.
.. seealso:: See also:
    :ref:`Quick Tutorial Unit Testing <qtut_unit_testing>`, :ref:`Quick
@@ -772,38 +753,34 @@
development we might need to collect some output. In production we might need
to detect situations when other people use the site. We need *logging*.
Fortunately Pyramid uses the normal Python approach to logging. The scaffold
generated in your ``development.ini`` has a number of lines that configure the
Fortunately Pyramid uses the normal Python approach to logging. The ``development.ini`` file for your project has a number of lines that configure the
logging for you to some reasonable defaults. You then see messages sent by
Pyramid (for example, when a new request comes in).
Maybe you would like to log messages in your code? In your Python module,
import and set up the logging:
import and set up the logging in your ``views.py``:
.. literalinclude:: quick_tour/package/hello_world/views.py
.. literalinclude:: quick_tour/logging/hello_world/views/default.py
    :language: python
    :linenos:
    :lineno-start: 3
    :lineno-match:
    :lines: 3-4
You can now, in your code, log messages:
.. literalinclude:: quick_tour/package/hello_world/views.py
.. literalinclude:: quick_tour/logging/hello_world/views/default.py
    :language: python
    :linenos:
    :lineno-start: 9
    :lines: 9-10
    :lineno-match:
    :lines: 7-8
    :emphasize-lines: 2
This will log ``Some Message`` at a ``debug`` log level to the
This will log ``Some Message`` at a ``DEBUG`` log level to the
application-configured logger in your ``development.ini``. What controls that?
These emphasized sections in the configuration file:
.. literalinclude:: quick_tour/package/development.ini
.. literalinclude:: quick_tour/logging/development.ini
    :language: ini
    :linenos:
    :lineno-start: 36
    :lines: 36-52
    :lineno-match:
    :lines: 34-50
    :emphasize-lines: 1-2,14-17
Our application, a package named ``hello_world``, is set up as a logger and
@@ -812,7 +789,7 @@
.. code-block:: text
    2016-01-18 13:55:55,040 DEBUG [hello_world.views:10][waitress] Some Message
    2016-12-25 03:03:57,059 DEBUG [hello_world.views:8][waitress] Some Message
.. seealso:: See also:
    :ref:`Quick Tutorial Logging <qtut_logging>` and :ref:`logging_chapter`.
@@ -830,11 +807,10 @@
sessioning support <../narr/sessions>`. In our ``__init__.py`` we first import
the kind of sessioning we want:
.. literalinclude:: quick_tour/package/hello_world/__init__.py
.. literalinclude:: quick_tour/sessions/hello_world/__init__.py
    :language: python
    :linenos:
    :lineno-start: 2
    :lines: 2-3
    :lineno-match:
    :lines: 1-2
    :emphasize-lines: 2
.. warning::
@@ -845,31 +821,28 @@
Now make a "factory" and pass it to the :term:`configurator`'s
``session_factory`` argument:
.. literalinclude:: quick_tour/package/hello_world/__init__.py
.. literalinclude:: quick_tour/sessions/hello_world/__init__.py
    :language: python
    :linenos:
    :lineno-start: 13
    :lines: 13-17
    :emphasize-lines: 3-5
    :lineno-match:
    :lines: 9-12
    :emphasize-lines: 2-3
Pyramid's :term:`request` object now has a ``session`` attribute that we can
use in our view code in ``views.py``:
.. literalinclude:: quick_tour/package/hello_world/views.py
.. literalinclude:: quick_tour/sessions/hello_world/views/default.py
    :language: python
    :linenos:
    :lineno-start: 9
    :lines: 9-15
    :lineno-match:
    :lines: 7-
    :emphasize-lines: 3-7
We need to update our Jinja2 template to show counter increment in the session:
We need to update our Jinja2 template ``templates/mytemplate.jinja2`` to show counter increment in the session:
.. literalinclude:: quick_tour/package/hello_world/templates/mytemplate.jinja2
.. literalinclude:: quick_tour/sessions/hello_world/templates/mytemplate.jinja2
    :language: jinja
    :linenos:
    :lineno-start: 40
    :lines: 40-42
    :emphasize-lines: 3
    :lineno-match:
    :lines: 4-8
    :emphasize-lines: 4
.. seealso:: See also:
    :ref:`Quick Tutorial Sessions <qtut_sessions>`, :ref:`sessions_chapter`,
@@ -885,13 +858,46 @@
usually leads to the mega-quality *SQLAlchemy*, a Python package that greatly
eases working with databases.
Pyramid and SQLAlchemy are great friends. That friendship includes a scaffold!
Pyramid and SQLAlchemy are great friends. That friendship includes a cookiecutter!
.. code-block:: bash
  $ $VENV/bin/pcreate --scaffold alchemy sqla_demo
  $ cd sqla_demo
  $ $VENV/bin/pip install -e .
    cd ~
    env/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout 1.10-branch
If prompted for the first item, accept the default ``yes`` by hitting return.
.. code-block:: text
    You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
    Is it okay to delete and re-clone it? [yes]: yes
    project_name [Pyramid Scaffold]: sqla_demo
    repo_name [sqla_demo]: sqla_demo
    Select template_language:
    1 - jinja2
    2 - chameleon
    3 - mako
    Choose from 1, 2, 3 [1]: 1
    Select backend:
    1 - none
    2 - sqlalchemy
    3 - zodb
    Choose from 1, 2, 3 [1]: 2
We then run through the following commands as before.
.. code-block:: bash
    # Change directory into your newly created project.
    cd sqla_demo
    # Create a new virtual environment...
    python3 -m venv env
    # ...where we upgrade packaging tools...
    env/bin/pip install --upgrade pip setuptools
    # ...and into which we install our project and its testing requirements.
    env/bin/pip install -e ".[testing]"
    # Reset our environment variable for a new virtual environment.
    export VENV=~/sqla_demo/env
We now have a working sample SQLAlchemy application with all dependencies
installed. The sample project provides a console script to initialize a SQLite
@@ -899,25 +905,25 @@
.. code-block:: bash
  $ $VENV/bin/initialize_sqla_demo_db development.ini
  $ $VENV/bin/pserve development.ini
    $VENV/bin/initialize_sqla_demo_db development.ini
    $VENV/bin/pserve development.ini
The ORM eases the mapping of database structures into a programming language.
SQLAlchemy uses "models" for this mapping. The scaffold generated a sample
SQLAlchemy uses "models" for this mapping. The cookiecutter generated a sample
model:
.. literalinclude:: quick_tour/sqla_demo/sqla_demo/models/mymodel.py
    :language: python
    :start-after: Start Sphinx Include
    :end-before: End Sphinx Include
    :lineno-match:
    :pyobject: MyModel
View code, which mediates the logic between web requests and the rest of the
system, can then easily get at the data thanks to SQLAlchemy:
.. literalinclude:: quick_tour/sqla_demo/sqla_demo/views/default.py
    :language: python
    :start-after: Start Sphinx Include
    :end-before: End Sphinx Include
    :lineno-match:
    :lines: 13
.. seealso:: See also:
    :ref:`Quick Tutorial Databases <qtut_databases>`, `SQLAlchemy
@@ -976,14 +982,10 @@
schemas, and validation. Recent versions of Deform also include a :ref:`retail
mode <deform:retail>` for gaining Deform features on custom forms.
Also the ``deform_bootstrap`` Pyramid add-on restyles the stock Deform widgets
using attractive CSS from Twitter Bootstrap and more powerful widgets from
Chosen.
Deform uses attractive CSS from Twitter Bootstrap and more powerful select, checkbox, and date and time widgets.
.. seealso:: See also:
    :ref:`Quick Tutorial Forms <qtut_forms>`, :ref:`Deform <deform:overview>`,
    :ref:`Colander <colander:overview>`, and `deform_bootstrap
    <https://pypi.python.org/pypi/deform_bootstrap>`_.
    :ref:`Quick Tutorial Forms <qtut_forms>`, :ref:`Deform <deform:overview>`, and :ref:`Colander <colander:overview>`.
Conclusion
==========