Steve Piercy
2016-04-16 d729f7d47b90d47d7e40fd5d1df449582f1c4eca
Merge pull request #2505 from stevepiercy/docs/quick-tour-gotcher-nose

Docs/quick tour gotcher nose
1 files deleted
27 files modified
1964 ■■■■ changed files
docs/conf.py 1 ●●●● patch | view | raw | blame | history
docs/glossary.rst 20 ●●●●● patch | view | raw | blame | history
docs/quick_tutorial/authentication.rst 81 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/authorization.rst 86 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/databases.rst 182 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/databases/sqltutorial.sqlite patch | view | raw | blame | history
docs/quick_tutorial/debugtoolbar.rst 53 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/forms.rst 119 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/functional_testing.rst 52 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/hello_world.rst 81 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/index.rst 10 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/ini.rst 92 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/jinja2.rst 55 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/json.rst 87 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/logging.rst 52 ●●●●● patch | view | raw | blame | history
docs/quick_tutorial/more_view_classes.rst 131 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/package.rst 40 ●●●●● patch | view | raw | blame | history
docs/quick_tutorial/request_response.rst 71 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/requirements.rst 138 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/routing.rst 53 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/scaffolds.rst 48 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/sessions.rst 70 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/static_assets.rst 45 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/templating.rst 85 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/tutorial_approach.rst 62 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/unit_testing.rst 108 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/view_classes.rst 65 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/views.rst 77 ●●●● patch | view | raw | blame | history
docs/conf.py
@@ -67,6 +67,7 @@
    'jinja2': ('http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/', None),
    'pylonswebframework': ('http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/', None),
    'python': ('https://docs.python.org/3', None),
    'pytest': ('http://pytest.org/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),
docs/glossary.rst
@@ -1108,14 +1108,15 @@
      ``f``, ``false``, ``n``, ``no``, ``off`` and ``0``.
   pip
      The `Python Packaging Authority's <https://www.pypa.io/>`_ recommended
      tool for installing Python packages.
      The :term:`Python Packaging Authority`'s recommended tool for installing
      Python packages.
   pyvenv
      The Python Packaging Authority formerly recommended using this command
      for `creating virtual environments on Python 3.4 and 3.5
      The :term:`Python Packaging Authority` formerly recommended using the
      ``pyvenv`` command for `creating virtual environments on Python 3.4 and
      3.5
      <https://packaging.python.org/en/latest/installing/#creating-virtual-environments>`_,
      but it is deprecated in 3.6 in favor of ``python3 -m venv`` on UNIX or
      but it was deprecated in 3.6 in favor of ``python3 -m venv`` on UNIX or
      ``python -m venv`` on Windows, which is backward compatible on Python
      3.3 and greater.
@@ -1124,9 +1125,14 @@
      use by a particular application, rather than being installed system wide.
   venv
      The `Python Packaging Authority's <https://www.pypa.io/>`_ recommended
      tool for creating virtual environments on Python 3.3 and greater.
      The :term:`Python Packaging Authority`'s recommended tool for creating
      virtual environments on Python 3.3 and greater.
      Note: whenever you encounter commands prefixed with ``$VENV`` (Unix)
      or ``%VENV`` (Windows), know that that is the environment variable whose
      value is the root of the virtual environment in question.
   Python Packaging Authority
      The `Python Packaging Authority (PyPA) <https://www.pypa.io/en/latest/>`_
      is a working group that maintains many of the relevant projects in Python
      packaging.
docs/quick_tutorial/authentication.rst
@@ -4,28 +4,27 @@
20: Logins With Authentication
==============================
Login views that authenticate a username/password against a list of
users.
Login views that authenticate a username and password against a list of users.
Background
==========
Most web applications have URLs that allow people to add/edit/delete
content via a web browser. Time to add
:ref:`security <security_chapter>`
to the application. In this first step we introduce authentication.
That is, logging in and logging out using Pyramid's rich facilities for
pluggable user storages.
Most web applications have URLs that allow people to add/edit/delete content
via a web browser. Time to add :ref:`security <security_chapter>` to the
application. In this first step we introduce authentication. That is, logging
in and logging out, using Pyramid's rich facilities for pluggable user storage.
In the next step we will introduce protection resources with
authorization security statements.
In the next step we will introduce protection of resources with authorization
security statements.
Objectives
==========
- Introduce the Pyramid concepts of authentication
- Introduce the Pyramid concepts of authentication.
- Create login/logout views
- Create login and logout views.
Steps
=====
@@ -38,22 +37,20 @@
    $ $VENV/bin/pip install -e .
#. Put the security hash in the ``authentication/development.ini``
   configuration file as ``tutorial.secret`` instead of putting it in
   the code:
   configuration file as ``tutorial.secret`` instead of putting it in the code:
   .. literalinclude:: authentication/development.ini
    :language: ini
    :linenos:
#. Get authentication (and for now, authorization policies) and login
   route into the :term:`configurator` in
   ``authentication/tutorial/__init__.py``:
#. Get authentication (and for now, authorization policies) and login route
   into the :term:`configurator` in ``authentication/tutorial/__init__.py``:
   .. literalinclude:: authentication/tutorial/__init__.py
    :linenos:
#. Create a ``authentication/tutorial/security.py`` module that can find
   our user information by providing an *authentication policy callback*:
#. Create an ``authentication/tutorial/security.py`` module that can find our
   user information by providing an *authentication policy callback*:
   .. literalinclude:: authentication/tutorial/security.py
    :linenos:
@@ -69,7 +66,7 @@
    :language: html
    :linenos:
#. Provide a login/logout box in ``authentication/tutorial/home.pt``
#. Provide a login/logout box in ``authentication/tutorial/home.pt``:
   .. literalinclude:: authentication/tutorial/home.pt
    :language: html
@@ -95,39 +92,37 @@
Analysis
========
Unlike many web frameworks, Pyramid includes a built-in but optional
security model for authentication and authorization. This security
system is intended to be flexible and support many needs. In this
security model, authentication (who are you) and authorization (what
are you allowed to do) are not just pluggable, but de-coupled. To learn
one step at a time, we provide a system that identifies users and lets
them log out.
Unlike many web frameworks, Pyramid includes a built-in but optional security
model for authentication and authorization. This security system is intended to
be flexible and support many needs. In this security model, authentication (who
are you) and authorization (what are you allowed to do) are not just pluggable,
but de-coupled. To learn one step at a time, we provide a system that
identifies users and lets them log out.
In this example we chose to use the bundled
:ref:`AuthTktAuthenticationPolicy <authentication_module>`
policy. We enabled it in our configuration and provided a
ticket-signing secret in our INI file.
In this example we chose to use the bundled :ref:`AuthTktAuthenticationPolicy
<authentication_module>` policy. We enabled it in our configuration and
provided a ticket-signing secret in our INI file.
Our view class grew a login view. When you reached it via a GET,
it returned a login form. When reached via POST, it processed the
username and password against the "groupfinder" callable that we
registered in the configuration.
Our view class grew a login view. When you reached it via a ``GET`` request, it
returned a login form. When reached via ``POST``, it processed the submitted
username and password against the "groupfinder" callable that we registered in
the configuration.
In our template, we fetched the ``logged_in`` value from the view
class. We use this to calculate the logged-in user,
if any. In the template we can then choose to show a login link to
anonymous visitors or a logout link to logged-in users.
In our template, we fetched the ``logged_in`` value from the view class. We use
this to calculate the logged-in user, if any. In the template we can then
choose to show a login link to anonymous visitors or a logout link to logged-in
users.
Extra Credit
Extra credit
============
#. What is the difference between a user and a principal?
#. Can I use a database behind my ``groupfinder`` to look up principals?
#. Once I am logged in, does any user-centric information get jammed
   onto each request? Use ``import pdb; pdb.set_trace()`` to answer
   this.
#. Once I am logged in, does any user-centric information get jammed onto each
   request? Use ``import pdb; pdb.set_trace()`` to answer this.
.. seealso:: See also :ref:`security_chapter`,
   :ref:`AuthTktAuthenticationPolicy <authentication_module>`.
docs/quick_tutorial/authorization.rst
@@ -4,33 +4,35 @@
21: Protecting Resources With Authorization
===========================================
Assign security statements to resources describing the permissions
required to perform an operation.
Assign security statements to resources describing the permissions required to
perform an operation.
Background
==========
Our application has URLs that allow people to add/edit/delete content
via a web browser. Time to add security to the application. Let's
protect our add/edit views to require a login (username of
``editor`` and password of ``editor``). We will allow the other views
to continue working without a password.
Our application has URLs that allow people to add/edit/delete content via a web
browser. Time to add security to the application. Let's protect our add/edit
views to require a login (username of ``editor`` and password of ``editor``).
We will allow the other views to continue working without a password.
Objectives
==========
- Introduce the Pyramid concepts of authentication, authorization,
  permissions, and access control lists (ACLs)
- Introduce the Pyramid concepts of authentication, authorization, permissions,
  and access control lists (ACLs).
- Make a :term:`root factory` that returns an instance of our
  class for the top of the application
- Make a :term:`root factory` that returns an instance of our class for the top
  of the application.
- Assign security statements to our root resource
- Assign security statements to our root resource.
- Add a permissions predicate on a view
- Add a permissions predicate on a view.
- Provide a :term:`Forbidden view` to handle visiting a URL without
  adequate permissions
- Provide a :term:`Forbidden view` to handle visiting a URL without adequate
  permissions.
Steps
=====
@@ -42,14 +44,13 @@
    $ cd ..; cp -r authentication authorization; cd authorization
    $ $VENV/bin/pip install -e .
#. Start by changing ``authorization/tutorial/__init__.py`` to
   specify a root factory to the :term:`configurator`:
#. Start by changing ``authorization/tutorial/__init__.py`` to specify a root
   factory to the :term:`configurator`:
   .. literalinclude:: authorization/tutorial/__init__.py
    :linenos:
#. That means we need to implement
   ``authorization/tutorial/resources.py``
#. That means we need to implement ``authorization/tutorial/resources.py``:
   .. literalinclude:: authorization/tutorial/resources.py
    :linenos:
@@ -70,48 +71,47 @@
#. If you are still logged in, click the "Log Out" link.
#. Visit http://localhost:6543/howdy in a browser. You should be
   asked to login.
#. Visit http://localhost:6543/howdy in a browser. You should be asked to
   login.
Analysis
========
This simple tutorial step can be boiled down to the following:
- A view can require a *permission* (``edit``)
- A view can require a *permission* (``edit``).
- The context for our view (the ``Root``) has an access control list
  (ACL)
- The context for our view (the ``Root``) has an access control list (ACL).
- This ACL says that the ``edit`` permission is available on ``Root``
  to the ``group:editors`` *principal*
- This ACL says that the ``edit`` permission is available on ``Root``  to the
  ``group:editors`` *principal*.
- The registered ``groupfinder`` answers whether a particular user
  (``editor``) has a particular group (``group:editors``)
- The registered ``groupfinder`` answers whether a particular user (``editor``)
  has a particular group (``group:editors``).
In summary: ``hello`` wants ``edit`` permission, ``Root`` says
In summary, ``hello`` wants ``edit`` permission, ``Root`` says
``group:editors`` has ``edit`` permission.
Of course, this only applies on ``Root``. Some other part of the site
(a.k.a. *context*) might have a different ACL.
Of course, this only applies on ``Root``. Some other part of the site (a.k.a.
*context*) might have a different ACL.
If you are not logged in and visit ``/howdy``, you need to get
shown the login screen. How does Pyramid know what is the login page to
use? We explicitly told Pyramid that the ``login`` view should be used
by decorating the view with ``@forbidden_view_config``.
If you are not logged in and visit ``/howdy``, you need to get shown the login
screen. How does Pyramid know what is the login page to use? We explicitly told
Pyramid that the ``login`` view should be used by decorating the view with
``@forbidden_view_config``.
Extra Credit
Extra credit
============
#. Do I have to put a ``renderer`` in my ``@forbidden_view_config``
   decorator?
#. Do I have to put a ``renderer`` in my ``@forbidden_view_config`` decorator?
#. Perhaps you would like the experience of not having enough permissions
   (forbidden) to be richer. How could you change this?
#. Perhaps we want to store security statements in a database and
   allow editing via a browser. How might this be done?
#. Perhaps we want to store security statements in a database and allow editing
   via a browser. How might this be done?
#. What if we want different security statements on different kinds of
   objects? Or on the same kinds of objects, but in different parts of a
   URL hierarchy?
#. What if we want different security statements on different kinds of objects?
   Or on the same kinds of objects, but in different parts of a URL hierarchy?
docs/quick_tutorial/databases.rst
@@ -4,37 +4,39 @@
19: Databases Using SQLAlchemy
==============================
Store/retrieve data using the SQLAlchemy ORM atop the SQLite database.
Store and retrieve data using the SQLAlchemy ORM atop the SQLite database.
Background
==========
Our Pyramid-based wiki application now needs database-backed storage of
pages. This frequently means a SQL database. The Pyramid community
strongly supports the
:ref:`SQLAlchemy <sqla:index_toplevel>` project and its
:ref:`object-relational mapper (ORM) <sqla:ormtutorial_toplevel>`
as a convenient, Pythonic way to interface to databases.
Our Pyramid-based wiki application now needs database-backed storage of pages.
This frequently means an SQL database. The Pyramid community strongly supports
the :ref:`SQLAlchemy <sqla:index_toplevel>` project and its
:ref:`object-relational mapper (ORM) <sqla:ormtutorial_toplevel>` as a
convenient, Pythonic way to interface to databases.
In this step we hook up SQLAlchemy to a SQLite database table,
providing storage and retrieval for the wikipages in the previous step.
In this step we hook up SQLAlchemy to a SQLite database table, providing
storage and retrieval for the wiki pages in the previous step.
.. note::
    The ``alchemy`` scaffold is really helpful for getting a
    SQLAlchemy project going, including generation of the console
    script. Since we want to see all the decisions, we will forgo
    convenience in this tutorial and wire it up ourselves.
    The ``alchemy`` scaffold is really helpful for getting an SQLAlchemy
    project going, including generation of the console script. Since we want to
    see all the decisions, we will forgo convenience in this tutorial, and wire
    it up ourselves.
Objectives
==========
- Store pages in SQLite by using SQLAlchemy models
- Store pages in SQLite by using SQLAlchemy models.
- Use SQLAlchemy queries to list/add/view/edit pages
- Use SQLAlchemy queries to list/add/view/edit pages.
- Provide a database-initialize command by writing a Pyramid *console
  script* which can be run from the command line
- Provide a database-initialize command by writing a Pyramid *console script*
  which can be run from the command line.
Steps
=====
@@ -45,31 +47,31 @@
    $ cd ..; cp -r forms databases; cd databases
#. We need to add some dependencies in ``databases/setup.py`` as well
   as an "entry point" for the command-line script:
#. We need to add some dependencies in ``databases/setup.py`` as well as an
   "entry point" for the command-line script:
   .. literalinclude:: databases/setup.py
    :linenos:
   .. note::
     We aren't yet doing ``$VENV/bin/pip install -e .`` as we
     will change it later.
     We aren't yet doing ``$VENV/bin/pip install -e .`` as we will change it
     later.
#. Our configuration file at ``databases/development.ini`` wires
   together some new pieces:
#. Our configuration file at ``databases/development.ini`` wires together some
   new pieces:
   .. literalinclude:: databases/development.ini
    :language: ini
#. This engine configuration now needs to be read into the application
   through changes in ``databases/tutorial/__init__.py``:
#. This engine configuration now needs to be read into the application through
   changes in ``databases/tutorial/__init__.py``:
   .. literalinclude:: databases/tutorial/__init__.py
    :linenos:
#. Make a command-line script at ``databases/tutorial/initialize_db.py``
   to initialize the database:
#. Make a command-line script at ``databases/tutorial/initialize_db.py`` to
   initialize the database:
   .. literalinclude:: databases/tutorial/initialize_db.py
    :linenos:
@@ -90,51 +92,49 @@
   .. code-block:: bash
    $ $VENV/bin/initialize_tutorial_db development.ini
    2015-06-01 11:22:52,650 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
    2015-06-01 11:22:52,650 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2015-06-01 11:22:52,651 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
    2015-06-01 11:22:52,651 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2015-06-01 11:22:52,652 INFO  [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages")
    2015-06-01 11:22:52,652 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2015-06-01 11:22:52,653 INFO  [sqlalchemy.engine.base.Engine][MainThread]
    2016-04-16 13:01:33,055 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
    2016-04-16 13:01:33,055 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2016-04-16 13:01:33,056 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
    2016-04-16 13:01:33,056 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2016-04-16 13:01:33,057 INFO  [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages")
    2016-04-16 13:01:33,057 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2016-04-16 13:01:33,058 INFO  [sqlalchemy.engine.base.Engine][MainThread]
    CREATE TABLE wikipages (
      uid INTEGER NOT NULL,
      title TEXT,
      body TEXT,
      PRIMARY KEY (uid),
      UNIQUE (title)
            uid INTEGER NOT NULL,
            title TEXT,
            body TEXT,
            PRIMARY KEY (uid),
            UNIQUE (title)
    )
    2015-06-01 11:22:52,653 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2015-06-01 11:22:52,655 INFO  [sqlalchemy.engine.base.Engine][MainThread] COMMIT
    2015-06-01 11:22:52,658 INFO  [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit)
    2015-06-01 11:22:52,659 INFO  [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO wikipages (title, body) VALUES (?, ?)
    2015-06-01 11:22:52,659 INFO  [sqlalchemy.engine.base.Engine][MainThread] ('Root', '<p>Root</p>')
    2015-06-01 11:22:52,659 INFO  [sqlalchemy.engine.base.Engine][MainThread] COMMIT
    2016-04-16 13:01:33,058 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2016-04-16 13:01:33,059 INFO  [sqlalchemy.engine.base.Engine][MainThread] COMMIT
    2016-04-16 13:01:33,062 INFO  [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit)
    2016-04-16 13:01:33,062 INFO  [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO wikipages (title, body) VALUES (?, ?)
    2016-04-16 13:01:33,063 INFO  [sqlalchemy.engine.base.Engine][MainThread] ('Root', '<p>Root</p>')
    2016-04-16 13:01:33,063 INFO  [sqlalchemy.engine.base.Engine][MainThread] COMMIT
#. With our data now driven by SQLAlchemy queries, we need to update
   our ``databases/tutorial/views.py``:
#. With our data now driven by SQLAlchemy queries, we need to update our
   ``databases/tutorial/views.py``:
   .. literalinclude:: databases/tutorial/views.py
    :linenos:
#. Our tests in ``databases/tutorial/tests.py`` changed to include
   SQLAlchemy bootstrapping:
#. Our tests in ``databases/tutorial/tests.py`` changed to include SQLAlchemy
   bootstrapping:
   .. literalinclude:: databases/tutorial/tests.py
    :linenos:
#. Run the tests in your package using ``nose``:
#. Run the tests in your package using ``py.test``:
    .. code-block:: bash
   .. code-block:: bash
        $ $VENV/bin/nosetests tutorial
        ..
        -----------------------------------------------------------------
        Ran 2 tests in 1.141s
        OK
    $ $VENV/bin/py.test tutorial/tests.py -q
    ..
    2 passed in 1.41 seconds
#. Run your Pyramid application with:
@@ -144,57 +144,55 @@
#. Open http://localhost:6543/ in a browser.
Analysis
========
Let's start with the dependencies. We made the decision to use
``SQLAlchemy`` to talk to our database. We also, though, installed
``pyramid_tm`` and ``zope.sqlalchemy``. Why?
Let's start with the dependencies. We made the decision to use ``SQLAlchemy``
to talk to our database. We also, though, installed ``pyramid_tm`` and
``zope.sqlalchemy``. Why?
Pyramid has a strong orientation towards support for ``transactions``.
Specifically, you can install a transaction manager into your
application either as middleware or a Pyramid "tween". Then,
just before you return the response, all transaction-aware parts of
your application are executed.
Specifically, you can install a transaction manager into your application
either as middleware or a Pyramid "tween". Then, just before you return the
response, all transaction-aware parts of your application are executed.
This means Pyramid view code usually doesn't manage transactions. If
your view code or a template generates an error, the transaction manager
aborts the transaction. This is a very liberating way to write code.
This means Pyramid view code usually doesn't manage transactions. If your view
code or a template generates an error, the transaction manager aborts the
transaction. This is a very liberating way to write code.
The ``pyramid_tm`` package provides a "tween" that is configured in the
``development.ini`` configuration file. That installs it. We then need
a package that makes SQLAlchemy, and thus the RDBMS transaction manager,
integrate with the Pyramid transaction manager. That's what
``zope.sqlalchemy`` does.
``development.ini`` configuration file. That installs it. We then need a
package that makes SQLAlchemy, and thus the RDBMS transaction manager,
integrate with the Pyramid transaction manager. That's what ``zope.sqlalchemy``
does.
Where do we point at the location on disk for the SQLite file? In the
configuration file. This lets consumers of our package change the
location in a safe (non-code) way. That is, in configuration. This
configuration-oriented approach isn't required in Pyramid; you can
still make such statements in your ``__init__.py`` or some companion
module.
configuration file. This lets consumers of our package change the location in a
safe (non-code) way. That is, in configuration. This configuration-oriented
approach isn't required in Pyramid; you can still make such statements in your
``__init__.py`` or some companion module.
The ``initialize_tutorial_db`` is a nice example of framework support.
You point your setup at the location of some ``[console_scripts]`` and
these get generated into your virtual environment's ``bin`` directory. Our
console script follows the pattern of being fed a configuration file
with all the bootstrapping. It then opens SQLAlchemy and creates the
root of the wiki, which also makes the SQLite file. Note the
``with transaction.manager`` part that puts the work in the scope of a
transaction, as we aren't inside a web request where this is done
automatically.
The ``initialize_tutorial_db`` is a nice example of framework support. You
point your setup at the location of some ``[console_scripts]``, and these get
generated into your virtual environment's ``bin`` directory. Our console script
follows the pattern of being fed a configuration file with all the
bootstrapping. It then opens SQLAlchemy and creates the root of the wiki, which
also makes the SQLite file. Note the ``with transaction.manager`` part that
puts the work in the scope of a transaction, as we aren't inside a web request
where this is done automatically.
The ``models.py`` does a little bit extra work to hook up SQLAlchemy
into the Pyramid transaction manager. It then declares the model for a
``Page``.
The ``models.py`` does a little bit of extra work to hook up SQLAlchemy into
the Pyramid transaction manager. It then declares the model for a ``Page``.
Our views have changes primarily around replacing our dummy
dictionary-of-dictionaries data with proper database support: list the
rows, add a row, edit a row, and delete a row.
dictionary-of-dictionaries data with proper database support: list the rows,
add a row, edit a row, and delete a row.
Extra Credit
Extra credit
============
#. Why all this code? Why can't I just type 2 lines and have magic ensue?
#. Why all this code? Why can't I just type two lines and have magic ensue?
#. Give a try at a button that deletes a wiki page.
docs/quick_tutorial/databases/sqltutorial.sqlite
Binary files differ
docs/quick_tutorial/debugtoolbar.rst
@@ -6,31 +6,34 @@
Error handling and introspection using the ``pyramid_debugtoolbar`` add-on.
Background
==========
As we introduce the basics we also want to show how to be productive in
development and debugging. For example, we just discussed template
reloading and earlier we showed ``--reload`` for application reloading.
As we introduce the basics, we also want to show how to be productive in
development and debugging. For example, we just discussed template reloading,
and earlier we showed ``--reload`` for application reloading.
``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes
several tools available in your browser. Adding it to your project
illustrates several points about configuration.
``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes several tools
available in your browser. Adding it to your project illustrates several points
about configuration.
Objectives
==========
- Install and enable the toolbar to help during development
- Install and enable the toolbar to help during development.
- Explain Pyramid add-ons
- Explain Pyramid add-ons.
- Show how an add-on gets configured into your application
- Show how an add-on gets configured into your application.
Steps
=====
#. First we copy the results of the previous step, as well as install
   the ``pyramid_debugtoolbar`` package:
#. First we copy the results of the previous step, as well as install the
   ``pyramid_debugtoolbar`` package:
   .. code-block:: bash
@@ -54,6 +57,7 @@
#. Open http://localhost:6543/ in your browser. See the handy
   toolbar on the right.
Analysis
========
@@ -66,16 +70,16 @@
means we need to include its add-on configuration into our web application. We
could do this with imperative configuration in ``tutorial/__init__.py`` by
using ``config.include``. Pyramid also supports wiring in add-on configuration
via our ``development.ini`` using ``pyramid.includes``. We use this to load
the configuration for the debugtoolbar.
via our ``development.ini`` using ``pyramid.includes``. We use this to load the
configuration for the debugtoolbar.
You'll now see an attractive button on the right side of your browser, which
you may click to provide introspective access to debugging information in a
new browser tab. Even better, if your web application generates an error, you
will see a nice traceback on the screen. When you want to disable this
toolbar, there's no need to change code: you can remove it from
``pyramid.includes`` in the relevant ``.ini`` configuration file (thus showing
why configuration files are handy.)
you may click to provide introspective access to debugging information in a new
rowser tab. Even better, if your web application generates an error, you will
see a nice traceback on the screen. When you want to disable this toolbar,
there's no need to change code: you can remove it from ``pyramid.includes`` in
the relevant ``.ini`` configuration file (thus showing why configuration files
are handy).
Note that the toolbar injects a small amount of HTML/CSS into your app just
before the closing ``</body>`` tag in order to display itself. If you start to
@@ -85,13 +89,14 @@
.. seealso:: See also :ref:`pyramid_debugtoolbar <toolbar:overview>`.
Extra Credit
============
#. Why don't we add ``pyramid_debugtoolbar`` to the list of
   ``install_requires`` dependencies in ``debugtoolbar/setup.py``?
#. Introduce a bug into your application:  Change:
#. Introduce a bug into your application. Change:
   .. code-block:: python
@@ -105,7 +110,7 @@
    def hello_world(request):
        return xResponse('<body><h1>Hello World!</h1></body>')
   Save, and visit http://localhost:6543/ again.  Notice the nice
   traceback display.  On the lowest line, click the "screen" icon to the
   right, and try typing the variable names ``request`` and ``Response``.
   What else can you discover?
   Save, and visit http://localhost:6543/ again. Notice the nice traceback
   display. On the lowest line, click the "screen" icon to the right, and try
   typing the variable names ``request`` and ``Response``. What else can you
   discover?
docs/quick_tutorial/forms.rst
@@ -6,30 +6,30 @@
Schema-driven, autogenerated forms with validation.
Background
==========
Modern web applications deal extensively with forms. Developers,
though, have a wide range of philosophies about how frameworks should
help them with their forms. As such, Pyramid doesn't directly bundle
one particular form library. Instead there are a variety of form
libraries that are easy to use in Pyramid.
Modern web applications deal extensively with forms. Developers, though, have a
wide range of philosophies about how frameworks should help them with their
forms. As such, Pyramid doesn't directly bundle one particular form library.
Instead there are a variety of form libraries that are easy to use in Pyramid.
:ref:`Deform <deform:overview>`
is one such library. In this step, we introduce Deform for our
forms and validation. This also gives us :ref:`Colander <colander:overview>`
:ref:`Deform <deform:overview>` is one such library. In this step, we introduce
Deform for our forms. This also gives us :ref:`Colander <colander:overview>`
for schemas and validation.
Deform is getting a facelift, with styling from Twitter Bootstrap and
advanced widgets from popular JavaScript projects. The work began in
``deform_bootstrap`` and is being merged into an update to Deform.
Deform uses styling from Twitter Bootstrap and advanced widgets from popular
JavaScript projects.
Objectives
==========
- Make a schema using Colander, the companion to Deform
- Make a schema using Colander, the companion to Deform.
- Create a form with Deform and change our views to handle validation
- Create a form with Deform and change our views to handle validation.
Steps
=====
@@ -40,8 +40,8 @@
    $ cd ..; cp -r view_classes forms; cd forms
#. Let's edit ``forms/setup.py`` to declare a dependency on Deform
   (which then pulls in Colander as a dependency:
#. Let's edit ``forms/setup.py`` to declare a dependency on Deform (which then
   pulls in Colander as a dependency:
   .. literalinclude:: forms/setup.py
    :linenos:
@@ -52,21 +52,19 @@
      $ $VENV/bin/pip install -e .
#. Register a static view in ``forms/tutorial/__init__.py`` for
   Deform's CSS/JS etc. as well as our demo wikipage scenario's
   views:
#. Register a static view in ``forms/tutorial/__init__.py`` for Deform's CSS,
   JavaScript, etc., as well as our demo wiki page's views:
   .. literalinclude:: forms/tutorial/__init__.py
    :linenos:
#. Implement the new views, as well as the form schemas and some
   dummy data, in ``forms/tutorial/views.py``:
#. Implement the new views, as well as the form schemas and some dummy data, in
   ``forms/tutorial/views.py``:
   .. literalinclude:: forms/tutorial/views.py
    :linenos:
#. A template for the top of the "wiki" in
   ``forms/tutorial/wiki_view.pt``:
#. A template for the top of the "wiki" in ``forms/tutorial/wiki_view.pt``:
   .. literalinclude:: forms/tutorial/wiki_view.pt
    :language: html
@@ -79,12 +77,20 @@
    :language: html
    :linenos:
#. Finally, a template at ``forms/tutorial/wikipage_view.pt``
   for viewing a wiki page:
#. Finally, a template at ``forms/tutorial/wikipage_view.pt`` for viewing a
   wiki page:
   .. literalinclude:: forms/tutorial/wikipage_view.pt
    :language: html
    :linenos:
#. Run the tests:
   .. code-block:: bash
    $ $VENV/bin/py.test tutorial/tests.py -q
    ..
    2 passed in 0.45 seconds
#. Run your Pyramid application with:
@@ -98,51 +104,50 @@
Analysis
========
This step helps illustrate the utility of asset specifications for
static assets. We have an outside package called Deform with static
assets which need to be published. We don't have to know where on disk
it is located. We point at the package, then the path inside the package.
This step helps illustrate the utility of asset specifications for static
assets. We have an outside package called Deform with static assets which need
to be published. We don't have to know where on disk it is located. We point at
the package, then the path inside the package.
We just need to include a call to ``add_static_view`` to make that
directory available at a URL. For Pyramid-specific packages,
Pyramid provides a facility (``config.include()``) which even makes
that unnecessary for consumers of a package. (Deform is not specific to
Pyramid.)
We just need to include a call to ``add_static_view`` to make that directory
available at a URL. For Pyramid-specific packages, Pyramid provides a facility
(``config.include()``) which even makes that unnecessary for consumers of a
package. (Deform is not specific to Pyramid.)
Our forms have rich widgets which need the static CSS and JS just
mentioned. Deform has a :term:`resource registry` which allows widgets
to specify which JS and CSS are needed. Our ``wikipage_addedit.pt``
template shows how we iterated over that data to generate markup that
includes the needed resources.
Our forms have rich widgets which need the static CSS and JavaScript just
mentioned. Deform has a :term:`resource registry` which allows widgets to
specify which JavaScript and CSS are needed. Our ``wikipage_addedit.pt``
template shows how we iterated over that data to generate markup that includes
the needed resources.
Our add and edit views use a pattern called *self-posting forms*.
Meaning, the same URL is used to ``GET`` the form as is used to
``POST`` the form. The route, the view, and the template are the same
whether you are walking up to it the first time or you clicked a button.
Our add and edit views use a pattern called *self-posting forms*. Meaning, the
same URL is used to ``GET`` the form as is used to ``POST`` the form. The
route, the view, and the template are the same URL whether you are walking up
to it for the first time or you clicked a button.
Inside the view we do ``if 'submit' in self.request.params:`` to see if
this form was a ``POST`` where the user clicked on a particular button
Inside the view we do ``if 'submit' in self.request.params:`` to see if this
form was a ``POST`` where the user clicked on a particular button
``<input name="submit">``.
The form controller then follows a typical pattern:
- If you are doing a GET, skip over and just return the form
- If you are doing a ``GET``, skip over and just return the form.
- If you are doing a POST, validate the form contents
- If you are doing a ``POST``, validate the form contents.
- If the form is invalid, bail out by re-rendering the form with the
  supplied ``POST`` data
- If the form is invalid, bail out by re-rendering the form with the supplied
  ``POST`` data.
- If the validation succeeded, perform some action and issue a
  redirect via ``HTTPFound``.
- If the validation succeeded, perform some action and issue a redirect via
  ``HTTPFound``.
We are, in essence, writing our own form controller. Other
Pyramid-based systems, including ``pyramid_deform``, provide a
form-centric view class which automates much of this branching and
routing.
We are, in essence, writing our own form controller. Other Pyramid-based
systems, including ``pyramid_deform``, provide a form-centric view class which
automates much of this branching and routing.
Extra Credit
Extra credit
============
#. Give a try at a button that goes to a delete view for a
   particular wiki page.
#. Give a try at a button that goes to a delete view for a particular wiki
   page.
docs/quick_tutorial/functional_testing.rst
@@ -6,30 +6,33 @@
Write end-to-end full-stack testing using ``webtest``.
Background
==========
Unit tests are a common and popular approach to test-driven development
(TDD). In web applications, though, the templating and entire apparatus
of a web site are important parts of the delivered quality. We'd like a
way to test these.
Unit tests are a common and popular approach to test-driven development (TDD).
In web applications, though, the templating and entire apparatus of a web site
are important parts of the delivered quality. We'd like a way to test these.
WebTest is a Python package that does functional testing. With WebTest
you can write tests which simulate a full HTTP request against a WSGI
application, then test the information in the response. For speed
purposes, WebTest skips the setup/teardown of an actual HTTP server,
providing tests that run fast enough to be part of TDD.
`WebTest <http://docs.pylonsproject.org/projects/webtest/en/latest/>`_ is a
Python package that does functional testing. With WebTest you can write tests
which simulate a full HTTP request against a WSGI application, then test the
information in the response. For speed purposes, WebTest skips the
setup/teardown of an actual HTTP server, providing tests that run fast enough
to be part of TDD.
Objectives
==========
- Write a test which checks the contents of the returned HTML
- Write a test which checks the contents of the returned HTML.
Steps
=====
#. First we copy the results of the previous step, as well as install
   the ``webtest`` package:
#. First we copy the results of the previous step, as well as install the
   ``webtest`` package:
   .. code-block:: bash
@@ -43,31 +46,28 @@
   .. literalinclude:: functional_testing/tutorial/tests.py
    :linenos:
   Be sure this file is not executable, or ``nosetests`` may not
   include your tests.
   Be sure this file is not executable, or ``pytest`` may not include your
   tests.
   
#. Now run the tests:
   .. code-block:: bash
    $ $VENV/bin/py.test tutorial/tests.py -q
    ..
    2 passed in 0.25 seconds
    $ $VENV/bin/nosetests tutorial
    .
    ----------------------------------------------------------------------
    Ran 2 tests in 0.141s
    OK
Analysis
========
We now have the end-to-end testing we were looking for. WebTest lets us
simply extend our existing ``nose``-based test approach with functional
tests that are reported in the same output. These new tests not only
cover our templating, but they didn't dramatically increase the
execution time of our tests.
We now have the end-to-end testing we were looking for. WebTest lets us simply
extend our existing ``pytest``-based test approach with functional tests that
are reported in the same output. These new tests not only cover our templating,
but they didn't dramatically increase the execution time of our tests.
Extra Credit
Extra credit
============
#. Why do our functional tests use ``b''``?
docs/quick_tutorial/hello_world.rst
@@ -4,40 +4,40 @@
01: Single-File Web Applications
================================
What's the simplest way to get started in Pyramid? A single-file module.
No Python packages, no ``pip install -e .``, no other machinery.
What's the simplest way to get started in Pyramid? A single-file module. No
Python packages, no ``pip install -e .``, no other machinery.
Background
==========
Microframeworks are all the rage these days. "Microframework" is a
marketing term, not a technical one.  They have a low mental overhead:
they do so little, the only things you have to worry about are *your
things*.
Microframeworks are all the rage these days. "Microframework" is a marketing
term, not a technical one. They have a low mental overhead: they do so little,
the only things you have to worry about are *your things*.
Pyramid is special because it can act as a single-file module
microframework. You can have a single Python file that can be executed
directly by Python. But Pyramid also provides facilities to scale to
the largest of applications.
Pyramid is special because it can act as a single-file module microframework.
You can have a single Python file that can be executed directly by Python. But
Pyramid also provides facilities to scale to the largest of applications.
Python has a standard called :term:`WSGI` that defines how
Python web applications plug into standard servers, getting passed
incoming requests and returning responses. Most modern Python web
frameworks obey an "MVC" (model-view-controller) application pattern,
where the data in the model has a view that mediates interaction with
outside systems.
Python has a standard called :term:`WSGI` that defines how Python web
applications plug into standard servers, getting passed incoming requests, and
returning responses. Most modern Python web frameworks obey an "MVC"
(model-view-controller) application pattern, where the data in the model has a
view that mediates interaction with outside systems.
In this step we'll see a brief glimpse of WSGI servers, WSGI
applications, requests, responses, and views.
In this step we'll see a brief glimpse of WSGI servers, WSGI applications,
requests, responses, and views.
Objectives
==========
- Get a running Pyramid web application, as simply as possible
- Get a running Pyramid web application, as simply as possible.
- Use that as a well-understood base for adding each unit of complexity
- Use that as a well-understood base for adding each unit of complexity.
- Initial exposure to WSGI apps, requests, views, and responses
- Initial exposure to WSGI apps, requests, views, and responses.
Steps
=====
@@ -64,30 +64,29 @@
#. Open http://localhost:6543/ in your browser.
Analysis
========
New to Python web programming? If so, some lines in module merit
New to Python web programming? If so, some lines in the module merit
explanation:
#. *Line 11*. The ``if __name__ == '__main__':`` is Python's way of
   saying "Start here when running from the command line", rather than
   when this module is imported.
#. *Line 11*. The ``if __name__ == '__main__':`` is Python's way of saying,
   "Start here when running from the command line", rather than when this
   module is imported.
#. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect
   :term:`view` code to a particular URL :term:`route`.
#. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect :term:`view`
   code to a particular URL :term:`route`.
#. *Lines 6-8*. Implement the view code that generates the
   :term:`response`.
#. *Lines 6-8*. Implement the view code that generates the :term:`response`.
#. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP
   server.
#. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP server.
As shown in this example, the :term:`configurator` plays a
central role in Pyramid development. Building an application from
loosely-coupled parts via :ref:`configuration_narr` is a
central idea in Pyramid, one that we will revisit regularly in this
*Quick Tour*.
As shown in this example, the :term:`configurator` plays a central role in
Pyramid development. Building an application from loosely-coupled parts via
:ref:`configuration_narr` is a central idea in Pyramid, one that we will
revisit regularly in this *Quick Tutorial*.
Extra Credit
============
@@ -106,9 +105,9 @@
#. What happens if you return a string of HTML? A sequence of integers?
#. Put something invalid, such as ``print xyz``, in the view function.
   Kill your ``python app.py`` with ``cntrl-c`` and restart,
   then reload your browser. See the exception in the console?
#. Put something invalid, such as ``print xyz``, in the view function. Kill
   your ``python app.py`` with ``ctrl-C`` and restart, then reload your
   browser. See the exception in the console?
#. The ``GI`` in ``WSGI`` stands for "Gateway Interface". What web
   standard is this modelled after?
#. The ``GI`` in ``WSGI`` stands for "Gateway Interface". What web standard is
   this modelled after?
docs/quick_tutorial/index.rst
@@ -4,12 +4,12 @@
Quick Tutorial for Pyramid
==========================
Pyramid is a web framework for Python 2 and 3. This tutorial gives a
Python 3/2-compatible, high-level tour of the major features.
Pyramid is a web framework for Python 2 and 3. This tutorial gives a Python
3/2-compatible, high-level tour of the major features.
This hands-on tutorial covers "a little about a lot": practical
introductions to the most common facilities. Fun, fast-paced, and most
certainly not aimed at experts of the Pyramid web framework.
This hands-on tutorial covers "a little about a lot": practical introductions
to the most common facilities. Fun, fast-paced, and most certainly not aimed at
experts of the Pyramid web framework.
Contents
========
docs/quick_tutorial/ini.rst
@@ -7,28 +7,30 @@
Use Pyramid's ``pserve`` command with a ``.ini`` configuration file for
simpler, better application running.
Background
==========
Pyramid has a first-class concept of
:ref:`configuration <configuration_narr>` distinct from code.
This approach is optional, but its presence makes it distinct from
other Python web frameworks. It taps into Python's ``setuptools``
library, which establishes conventions for installing and providing
"entry points" for Python projects. Pyramid uses an entry point to
let a Pyramid application know where to find the WSGI app.
Pyramid has a first-class concept of :ref:`configuration <configuration_narr>`
distinct from code. This approach is optional, but its presence makes it
distinct from other Python web frameworks. It taps into Python's ``setuptools``
library, which establishes conventions for installing and providing "entry
points" for Python projects. Pyramid uses an entry point to let a Pyramid
application know where to find the WSGI app.
Objectives
==========
- Modify our ``setup.py`` to have an entry point telling Pyramid the
  location of the WSGI app
- Modify our ``setup.py`` to have an entry point telling Pyramid the location
  of the WSGI app.
- Create an application driven by a ``.ini`` file
- Create an application driven by an ``.ini`` file.
- Startup the application with Pyramid's ``pserve`` command
- Start the application with Pyramid's ``pserve`` command.
- Move code into the package's ``__init__.py``
- Move code into the package's ``__init__.py``.
Steps
=====
@@ -39,14 +41,14 @@
    $ cd ..; cp -r package ini; cd ini
#. Our ``ini/setup.py`` needs a setuptools "entry point" in the
   ``setup()`` function:
#. Our ``ini/setup.py`` needs a setuptools "entry point" in the ``setup()``
   function:
   .. literalinclude:: ini/setup.py
    :linenos:
#. We can now install our project, thus generating (or re-generating) an
   "egg" at ``ini/tutorial.egg-info``:
#. We can now install our project, thus generating (or re-generating) an "egg"
   at ``ini/tutorial.egg-info``:
   .. code-block:: bash
@@ -58,8 +60,8 @@
    :language: ini
    :linenos:
#. We can refactor our startup code from the previous step's ``app.py``
   into ``ini/tutorial/__init__.py``:
#. We can refactor our startup code from the previous step's ``app.py`` into
   ``ini/tutorial/__init__.py``:
   .. literalinclude:: ini/tutorial/__init__.py
    :linenos:
@@ -81,27 +83,26 @@
Analysis
========
Our ``development.ini`` file is read by ``pserve`` and serves to
bootstrap our application. Processing then proceeds as described in
the Pyramid chapter on
Our ``development.ini`` file is read by ``pserve`` and serves to bootstrap our
application. Processing then proceeds as described in the Pyramid chapter on
:ref:`application startup <startup_chapter>`:
- ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial``
- ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial``.
- The projects's ``setup.py`` has defined an "entry point" (lines 9-12)
  for the project  "main" entry point of ``tutorial:main``
- The projects's ``setup.py`` has defined an "entry point" (lines 9-12) for the
  project's "main" entry point of ``tutorial:main``.
- The ``tutorial`` package's ``__init__`` has a ``main`` function
- The ``tutorial`` package's ``__init__`` has a ``main`` function.
- This function is invoked, with the values from certain ``.ini``
  sections passed in
- This function is invoked, with the values from certain ``.ini`` sections
  passed in.
The ``.ini`` file is also used for two other functions:
- *Configuring the WSGI server*. ``[server:main]`` wires up the choice of
  which WSGI *server* for your WSGI *application*. In this case, we are using
  ``wsgiref`` bundled in the Python library.  It also wires up the *port
  number*:   ``port = 6543`` tells ``wsgiref`` to listen on port 6543.
- *Configuring the WSGI server*. ``[server:main]`` wires up the choice of which
  WSGI *server* for your WSGI *application*. In this case, we are using
  ``wsgiref`` bundled in the Python library. It also wires up the *port
  number*: ``port = 6543`` tells ``wsgiref`` to listen on port 6543.
- *Configuring Python logging*. Pyramid uses Python standard logging, which
  needs a number of configuration values. The ``.ini`` serves this function.
@@ -109,27 +110,27 @@
  request.
We moved our startup code from ``app.py`` to the package's
``tutorial/__init__.py``. This isn't necessary,
but it is a common style in Pyramid to take the WSGI app bootstrapping
out of your module's code and put it in the package's ``__init__.py``.
``tutorial/__init__.py``. This isn't necessary, but it is a common style in
Pyramid to take the WSGI app bootstrapping out of your module's code and put it
in the package's ``__init__.py``.
The ``pserve`` application runner has a number of command-line arguments
and options. We are using ``--reload`` which tells ``pserve`` to watch
the filesystem for changes to relevant code (Python files, the INI file,
etc.) and, when something changes, restart the application. Very handy
during development.
The ``pserve`` application runner has a number of command-line arguments and
options. We are using ``--reload`` which tells ``pserve`` to watch the
filesystem for changes to relevant code (Python files, the INI file, etc.) and,
when something changes, restart the application. Very handy during development.
Extra Credit
============
#. If you don't like configuration and/or ``.ini`` files,
   could you do this yourself in Python code?
#. If you don't like configuration and/or ``.ini`` files, could you do this
   yourself in Python code?
#. Can we have multiple ``.ini`` configuration files for a project? Why
   might you want to do that?
#. Can we have multiple ``.ini`` configuration files for a project? Why might
   you want to do that?
#. The entry point in ``setup.py`` didn't mention ``__init__.py`` when
   it declared ``tutorial:main`` function. Why not?
#. The entry point in ``setup.py`` didn't mention ``__init__.py`` when it
   declared ``tutorial:main`` function. Why not?
#. What is the purpose of ``**settings``? What does the ``**`` signify?
@@ -139,4 +140,3 @@
   :ref:`what_is_this_pserve_thing`,
   :ref:`environment_chapter`,
   :ref:`paste_chapter`
docs/quick_tutorial/jinja2.rst
@@ -4,24 +4,26 @@
12: Templating With ``jinja2``
==============================
We just said Pyramid doesn't prefer one templating language over
another. Time to prove it. Jinja2 is a popular templating system,
used in Flask and modeled after Django's templates. Let's add
``pyramid_jinja2``, a Pyramid :term:`add-on` which enables Jinja2 as a
:term:`renderer` in our Pyramid applications.
We just said Pyramid doesn't prefer one templating language over another. Time
to prove it. Jinja2 is a popular templating system, used in Flask and modeled
after Django's templates. Let's add ``pyramid_jinja2``, a Pyramid
:term:`add-on` which enables Jinja2 as a :term:`renderer` in our Pyramid
applications.
Objectives
==========
- Show Pyramid's support for different templating systems
- Show Pyramid's support for different templating systems.
- Learn about installing Pyramid add-ons
- Learn about installing Pyramid add-ons.
Steps
=====
#. In this step let's start by copying the ``view_class`` step's
   directory, and then installing the ``pyramid_jinja2`` add-on.
#. In this step let's start by copying the ``view_class`` step's  directory,
   and then installing the ``pyramid_jinja2`` add-on.
   .. code-block:: bash
@@ -29,8 +31,7 @@
    $ $VENV/bin/pip install -e .
    $ $VENV/bin/pip install pyramid_jinja2
#. We need to include ``pyramid_jinja2`` in
   ``jinja2/tutorial/__init__.py``:
#. We need to include ``pyramid_jinja2`` in ``jinja2/tutorial/__init__.py``:
   .. literalinclude:: jinja2/tutorial/__init__.py
    :linenos:
@@ -49,7 +50,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.40 seconds
#. Run your Pyramid application with:
@@ -59,30 +62,30 @@
#. Open http://localhost:6543/ in your browser.
Analysis
========
Getting a Pyramid add-on into Pyramid is simple. First you use normal
Python package installation tools to install the add-on package into
your Python. You then tell Pyramid's configurator to run the setup code
Getting a Pyramid add-on into Pyramid is simple. First you use normal Python
package installation tools to install the add-on package into your Python
virtual environment. You then tell Pyramid's configurator to run the setup code
in the add-on. In this case the setup code told Pyramid to make a new
"renderer" available that looked for ``.jinja2`` file extensions.
Our view code stayed largely the same. We simply changed the file
extension on the renderer. For the template, the syntax for Chameleon
and Jinja2's basic variable insertion is very similar.
Our view code stayed largely the same. We simply changed the file extension on
the renderer. For the template, the syntax for Chameleon and Jinja2's basic
variable insertion is very similar.
Extra Credit
Extra credit
============
#. Our project now depends on ``pyramid_jinja2``. We installed that
   dependency manually. What is another way we could have made the
   association?
#. Our project now depends on ``pyramid_jinja2``. We installed that dependency
   manually. What is another way we could have made the association?
#. We used ``config.include`` which is an imperative configuration to get the
   :term:`Configurator` to load ``pyramid_jinja2``'s configuration.
   What is another way could include it into the config?
   :term:`Configurator` to load ``pyramid_jinja2``'s configuration. What is
   another way could include it into the config?
.. seealso:: `Jinja2 homepage <http://jinja.pocoo.org/>`_,
   and
.. seealso:: `Jinja2 homepage <http://jinja.pocoo.org/>`_, and
   :ref:`pyramid_jinja2 Overview <jinja2:overview>`
docs/quick_tutorial/json.rst
@@ -1,27 +1,28 @@
.. _qtut_json:
========================================
14: Ajax Development With JSON Renderers
14: AJAX Development With JSON Renderers
========================================
Modern web apps are more than rendered HTML. Dynamic pages now use
JavaScript to update the UI in the browser by requesting server data as
JSON. Pyramid supports this with a *JSON renderer*.
Modern web apps are more than rendered HTML. Dynamic pages now use JavaScript
to update the UI in the browser by requesting server data as JSON. Pyramid
supports this with a *JSON renderer*.
Background
==========
As we saw in :doc:`templating`, view declarations can specify a
renderer. Output from the view is then run through the renderer,
which generates and returns the ``Response``. We first used a Chameleon
renderer, then a Jinja2 renderer.
As we saw in :doc:`templating`, view declarations can specify a renderer.
Output from the view is then run through the renderer, which generates and
returns the response. We first used a Chameleon renderer, then a Jinja2
renderer.
Renderers aren't limited, however, to templates that generate HTML.
Pyramid supplies a JSON renderer which takes Python data,
serializes it to JSON, and performs some other functions such as
setting the content type. In fact, you can write your own renderer (or
extend a built-in renderer) containing custom logic for your unique
application.
Renderers aren't limited, however, to templates that generate HTML. Pyramid
supplies a JSON renderer which takes Python data, serializes it to JSON, and
performs some other functions such as setting the content type. In fact you can
write your own renderer (or extend a built-in renderer) containing custom logic
for your unique application.
Steps
=====
@@ -33,20 +34,18 @@
    $ cd ..; cp -r view_classes json; cd json
    $ $VENV/bin/pip install -e .
#. We add a new route for ``hello_json`` in
   ``json/tutorial/__init__.py``:
#. We add a new route for ``hello_json`` in ``json/tutorial/__init__.py``:
   .. literalinclude:: json/tutorial/__init__.py
    :linenos:
#. Rather than implement a new view, we will "stack" another decorator
   on the ``hello`` view in ``views.py``:
#. Rather than implement a new view, we will "stack" another decorator on the
   ``hello`` view in ``views.py``:
   .. literalinclude:: json/tutorial/views.py
    :linenos:
#. We need a new functional test at the end of
   ``json/tutorial/tests.py``:
#. We need a new functional test at the end of ``json/tutorial/tests.py``:
   .. literalinclude:: json/tutorial/tests.py
    :linenos:
@@ -55,7 +54,10 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/py.test tutorial/tests.py -q
    .....
    5 passed in 0.47 seconds
#. Run your Pyramid application with:
@@ -63,40 +65,39 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/howdy.json in your browser and you
   will see the resulting JSON response.
#. Open http://localhost:6543/howdy.json in your browser and you will see the
   resulting JSON response.
Analysis
========
Earlier we changed our view functions and methods to return Python
data. This change to a data-oriented view layer made test writing
easier, decoupling the templating from the view logic.
Earlier we changed our view functions and methods to return Python data. This
change to a data-oriented view layer made test writing easier, decoupling the
templating from the view logic.
Since Pyramid has a JSON renderer as well as the templating renderers,
it is an easy step to return JSON. In this case we kept the exact same
view and arranged to return a JSON encoding of the view data. We did
this by:
Since Pyramid has a JSON renderer as well as the templating renderers, it is an
easy step to return JSON. In this case we kept the exact same view and arranged
to return a JSON encoding of the view data. We did this by:
- Adding a route to map ``/howdy.json`` to a route name
- Adding a route to map ``/howdy.json`` to a route name.
- Providing a ``@view_config`` that associated that route name with an
  existing view
- Providing a ``@view_config`` that associated that route name with an existing
  view.
- *overriding* the view defaults in the view config that mentions the
  ``hello_json`` route, so that when the route is matched, we use the JSON
- *Overriding* the view defaults in the view config that mentions the
  ``hello_json`` route, so that when the route is matched, we use the JSON
  renderer rather than the ``home.pt`` template renderer that would otherwise 
  be used.
In fact, for pure Ajax-style web applications, we could re-use the existing
route by using Pyramid's view predicates to match on the
``Accepts:`` header sent by modern Ajax implementation.
In fact, for pure AJAX-style web applications, we could re-use the existing
route by using Pyramid's view predicates to match on the ``Accepts:`` header
sent by modern AJAX implementations.
Pyramid's JSON renderer uses the base Python JSON encoder,
thus inheriting its strengths and weaknesses. For example,
Python can't natively JSON encode DateTime objects. There are a number
of solutions for this in Pyramid, including extending the JSON renderer
with a custom renderer.
Pyramid's JSON renderer uses the base Python JSON encoder, thus inheriting its
strengths and weaknesses. For example, Python can't natively JSON encode
DateTime objects. There are a number of solutions for this in Pyramid,
including extending the JSON renderer with a custom renderer.
.. seealso:: :ref:`views_which_use_a_renderer`,
   :ref:`json_renderer`, and
docs/quick_tutorial/logging.rst
@@ -4,28 +4,30 @@
16: Collecting Application Info With Logging
============================================
Capture debugging and error output from your web applications using
standard Python logging.
Capture debugging and error output from your web applications using standard
Python logging.
Background
==========
It's important to know what is going on inside our web application.
In development we might need to collect some output. In production,
we might need to detect problems when other people use the site. We
need *logging*.
It's important to know what is going on inside our web application. In
development we might need to collect some output. In production, we might need
to detect problems when other people use the site. We need *logging*.
Fortunately Pyramid uses the normal Python approach to logging. The
scaffold generated in your ``development.ini`` 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.
Fortunately Pyramid uses the normal Python approach to logging. The scaffold
generated in your ``development.ini`` has a number of lines that configure the
logging for you to some reasonable defaults. You then see messages sent by
Pyramid, for example, when a new request comes in.
Objectives
==========
- Inspect the configuration setup used for logging
- Inspect the configuration setup used for logging.
- Add logging statements to your view code
- Add logging statements to your view code.
Steps
=====
@@ -42,8 +44,8 @@
   .. literalinclude:: logging/tutorial/views.py
    :linenos:
#. Finally let's edit ``development.ini`` configuration file
   to enable logging for our Pyramid application:
#. Finally let's edit ``development.ini`` configuration file to enable logging
   for our Pyramid application:
   .. literalinclude:: logging/development.ini
       :language: ini
@@ -52,7 +54,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.41 seconds
#. Run your Pyramid application with:
@@ -60,19 +64,21 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ and http://localhost:6543/howdy
   in your browser. Note, both in the console and in the debug
   toolbar, the message that you logged.
#. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser.
   Note, both in the console and in the debug toolbar, the message that you
   logged.
Analysis
========
In our configuration file ``development.ini``, our ``tutorial`` Python
package is setup as a logger and configured to log messages at a
``DEBUG`` or higher level. When you visit http://localhost:6543 your
console will now show::
In our configuration file ``development.ini``, our ``tutorial`` Python package
is set up as a logger and configured to log messages at a ``DEBUG`` or higher
level. When you visit http://localhost:6543, your console will now show:
 2013-08-09 10:42:42,968 DEBUG [tutorial.views][MainThread] In home view
.. code-block:: text
    2013-08-09 10:42:42,968 DEBUG [tutorial.views][MainThread] In home view
Also, if you have configured your Pyramid application to use the
``pyramid_debugtoolbar``, logging statements appear in one of its menus.
docs/quick_tutorial/more_view_classes.rst
@@ -6,48 +6,49 @@
Group views into a class, sharing configuration, state, and logic.
Background
==========
As part of its mission to help build more ambitious web applications,
Pyramid provides many more features for views and view classes.
As part of its mission to help build more ambitious web applications, Pyramid
provides many more features for views and view classes.
The Pyramid documentation discusses views as a Python "callable". This
callable can be a function, an object with an ``__call__``,
or a Python class. In this last case, methods on the class can be
decorated with ``@view_config`` to register the class methods with the
:term:`configurator` as a view.
The Pyramid documentation discusses views as a Python "callable". This callable
can be a function, an object with a ``__call__``, or a Python class. In this
last case, methods on the class can be decorated with ``@view_config`` to
register the class methods with the :term:`configurator` as a view.
At first, our views were simple, free-standing functions. Many times
your views are related: different ways to look at or work on the same
data or a REST API that handles multiple operations. Grouping these
together as a :ref:`view class <class_as_view>` makes sense:
At first, our views were simple, free-standing functions. Many times your views
are related: different ways to look at or work on the same data, or a REST API
that handles multiple operations. Grouping these together as a :ref:`view class
<class_as_view>` makes sense:
- Group views
- Group views.
- Centralize some repetitive defaults
- Centralize some repetitive defaults.
- Share some state and helpers
- Share some state and helpers.
Pyramid views have :ref:`view predicates <view_configuration_parameters>`
that determine which view is matched to a request, based on factors
such as the request method, the form parameters, etc. These predicates
provide many axes of flexibility.
Pyramid views have :ref:`view predicates <view_configuration_parameters>` that
determine which view is matched to a request, based on factors such as the
request method, the form parameters, and so on. These predicates provide many
axes of flexibility.
The following shows a simple example with four operations:
view a home page which leads to a form, save a change,
and press the delete button.
The following shows a simple example with four operations: view a home page
which leads to a form, save a change, and press the delete button.
Objectives
==========
- Group related views into a view class
- Group related views into a view class.
- Centralize configuration with class-level ``@view_defaults``
- Centralize configuration with class-level ``@view_defaults``.
- Dispatch one route/URL to multiple views based on request data
- Dispatch one route/URL to multiple views based on request data.
- Share states and logic between views and templates via the view class
- Share states and logic between views and templates via the view class.
Steps
=====
@@ -71,8 +72,7 @@
   .. literalinclude:: more_view_classes/tutorial/views.py
    :linenos:
#. Our primary view needs a template at
   ``more_view_classes/tutorial/home.pt``:
#. Our primary view needs a template at ``more_view_classes/tutorial/home.pt``:
   .. literalinclude:: more_view_classes/tutorial/home.pt
    :language: html
@@ -105,12 +105,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    .
    ----------------------------------------------------------------------
    Ran 2 tests in 0.248s
    OK
    $ $VENV/bin/py.test tutorial/tests.py -q
    ..
    2 passed in 0.40 seconds
#. Run your Pyramid application with:
@@ -118,29 +115,27 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/howdy/jane/doe in your browser. Click
   the ``Save`` and ``Delete`` buttons and watch the output in the
   console window.
#. Open http://localhost:6543/howdy/jane/doe in your browser. Click the
   ``Save`` and ``Delete`` buttons, and watch the output in the console window.
Analysis
========
As you can see, the four views are logically grouped together.
Specifically:
As you can see, the four views are logically grouped together. Specifically:
- We have a ``home`` view available at http://localhost:6543/ with
  a clickable link to the ``hello`` view.
- We have a ``home`` view available at http://localhost:6543/ with a clickable
  link to the ``hello`` view.
- The second view is returned when you go to ``/howdy/jane/doe``. This
  URL is
- The second view is returned when you go to ``/howdy/jane/doe``. This URL is
  mapped to the ``hello`` route that we centrally set using the optional
  ``@view_defaults``.
- The third view is returned when the form is submitted with a ``POST``
  method. This rule is specified in the ``@view_config`` for that view.
- The third view is returned when the form is submitted with a ``POST`` method.
  This rule is specified in the ``@view_config`` for that view.
- The fourth view is returned when clicking on a button such
  as ``<input type="submit" name="form.delete" value="Delete"/>``.
- The fourth view is returned when clicking on a button such as ``<input
  type="submit" name="form.delete" value="Delete"/>``.
In this step we show, using the following information as criteria, how to
decide which view to use:
@@ -149,49 +144,53 @@
- Parameter information in the request (submitted form field names)
We also centralize part of the view configuration to the class level
with ``@view_defaults``, then in one view, override that default just
for that one view. Finally, we put this commonality between views to
work in the view class by sharing:
We also centralize part of the view configuration to the class level with
``@view_defaults``, then in one view, override that default just for that one
view. Finally, we put this commonality between views to work in the view class
by sharing:
- State assigned in ``TutorialViews.__init__``
- A computed value
These are then available both in the view methods but also in the
templates (e.g. ``${view.view_name}`` and ``${view.full_name}``.
These are then available both in the view methods and in the templates (e.g.,
``${view.view_name}`` and ``${view.full_name}``).
As a note, we made a switch in our templates on how we generate URLs.
We previously hardcode the URLs, such as::
As a note, we made a switch in our templates on how we generate URLs. We
previously hardcoded the URLs, such as:
.. code-block:: html
  <a href="/howdy/jane/doe">Howdy</a>
In ``home.pt`` we switched to::
In ``home.pt`` we switched to:
.. code-block:: xml
  <a href="${request.route_url('hello', first='jane',
        last='doe')}">form</a>
Pyramid has rich facilities to help generate URLs in a flexible,
non-error-prone fashion.
Pyramid has rich facilities to help generate URLs in a flexible, non-error
prone fashion.
Extra Credit
Extra credit
============
#. Why could our template do ``${view.full_name}`` and not have to do
   ``${view.full_name()}``?
#. The ``edit`` and ``delete`` views are both submitted to with
   ``POST``. Why does the ``edit`` view configuration not catch the
   ``POST`` used by ``delete``?
#. The ``edit`` and ``delete`` views are both receive ``POST`` requests. Why
   does the ``edit`` view configuration not catch the ``POST`` used by
   ``delete``?
#. We used Python ``@property`` on ``full_name``. If we reference this
   many times in a template or view code, it would re-compute this
   every time. Does Pyramid provide something that will cache the initial
   computation on a property?
#. We used Python ``@property`` on ``full_name``. If we reference this many
   times in a template or view code, it would re-compute this every time. Does
   Pyramid provide something that will cache the initial computation on a
   property?
#. Can you associate more than one route with the same view?
#. There is also a ``request.route_path`` API.  How does this differ from
#. There is also a ``request.route_path`` API. How does this differ from
   ``request.route_url``?
.. seealso:: :ref:`class_as_view`, `Weird Stuff You Can Do With
docs/quick_tutorial/package.rst
@@ -3,16 +3,17 @@
============================================
Most modern Python development is done using Python packages, an approach
Pyramid puts to good use. In this step we redo "Hello World" as a
minimum Python package inside a minimum Python project.
Pyramid puts to good use. In this step we redo "Hello World" as a minimal
Python package inside a minimal Python project.
Background
==========
Python developers can organize a collection of modules and files into a
namespaced unit called a :ref:`package <python:tut-packages>`. If a
directory is on ``sys.path`` and has a special file named
``__init__.py``, it is treated as a Python package.
namespaced unit called a :ref:`package <python:tut-packages>`. If a directory
is on ``sys.path`` and has a special file named ``__init__.py``, it is treated
as a Python package.
Packages can be bundled up, made available for installation, and installed
through a toolchain oriented around a ``setup.py`` file. For this tutorial,
@@ -34,6 +35,7 @@
- That package will be part of a *project*.
Objectives
==========
@@ -42,6 +44,7 @@
- Get a minimum Python "project" in place by making a ``setup.py``.
- Install our ``tutorial`` project in development mode.
Steps
=====
@@ -56,8 +59,8 @@
   .. literalinclude:: package/setup.py
#. Make the new project installed for development then make a directory
   for the actual code:
#. Make the new project installed for development then make a directory for the
   actual code:
   .. code-block:: bash
@@ -80,26 +83,27 @@
#. Open http://localhost:6543/ in your browser.
Analysis
========
Python packages give us an organized unit of project development.
Python projects, via ``setup.py``, gives us special features when
our package is installed (in this case, in local development mode.)
Python packages give us an organized unit of project development. Python
projects, via ``setup.py``, give us special features when our package is
installed (in this case, in local development mode, also called local editable
mode as indicated by ``-e .``).
In this step we have a Python package called ``tutorial``. We use the
same name in each step of the tutorial, to avoid unnecessary retyping.
In this step we have a Python package called ``tutorial``. We use the same name
in each step of the tutorial, to avoid unnecessary retyping.
Above this ``tutorial`` directory we have the files that handle the
packaging of this project. At the moment, all we need is a
bare-bones ``setup.py``.
Above this ``tutorial`` directory we have the files that handle the packaging
of this project. At the moment, all we need is a bare-bones ``setup.py``.
Everything else is the same about our application. We simply made a
Python package with a ``setup.py`` and installed it in development mode.
Everything else is the same about our application. We simply made a Python
package with a ``setup.py`` and installed it in development mode.
Note that the way we're running the app (``python tutorial/app.py``) is a bit
of an odd duck.  We would never do this unless we were writing a tutorial that
tries to capture how this stuff works a step at a time.  It's generally a bad
tries to capture how this stuff works one step at a time. It's generally a bad
idea to run a Python module inside a package directly as a script.
.. seealso:: :ref:`Python Packages <python:tut-packages>` and `Working in
docs/quick_tutorial/request_response.rst
@@ -5,33 +5,32 @@
=======================================
Web applications handle incoming requests and return outgoing responses.
Pyramid makes working with requests and responses convenient and
reliable.
Pyramid makes working with requests and responses convenient and reliable.
Objectives
==========
- Learn the background on Pyramid's choices for requests and responses
- Learn the background on Pyramid's choices for requests and responses.
- Grab data out of the request
- Grab data out of the request.
- Change information in the response headers
- Change information in the response headers.
Background
==========
Developing for the web means processing web requests. As this is a
critical part of a web application, web developers need a robust,
mature set of software for web requests and returning web
responses.
Developing for the web means processing web requests. As this is a critical
part of a web application, web developers need a robust, mature set of software
for web requests and returning web responses.
Pyramid has always fit nicely into the existing world of Python web
development (virtual environments, packaging, scaffolding,
first to embrace Python 3, etc.) For request handling, Pyramid turned
to the well-regarded :term:`WebOb` Python library for request and
response handling. In our example
above, Pyramid hands ``hello_world`` a ``request`` that is
:ref:`based on WebOb <webob_chapter>`.
Pyramid has always fit nicely into the existing world of Python web development
(virtual environments, packaging, scaffolding, first to embrace Python 3, and
so on). Pyramid turned to the well-regarded :term:`WebOb` Python library for
request and response handling. In our example above, Pyramid hands
``hello_world`` a ``request`` that is :ref:`based on WebOb <webob_chapter>`.
Steps
=====
@@ -62,7 +61,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/py.test tutorial/tests.py -q
    .....
    5 passed in 0.30 seconds
#. Run your Pyramid application with:
@@ -70,37 +71,39 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ in your browser. You will be
   redirected to http://localhost:6543/plain
#. Open http://localhost:6543/ in your browser. You will be redirected to
   http://localhost:6543/plain.
#. Open http://localhost:6543/plain?name=alice in your browser.
Analysis
========
In this view class we have two routes and two views, with the first
leading to the second by an HTTP redirect. Pyramid can
:ref:`generate redirects <http_redirect>` by returning a
special object from a view or raising a special exception.
In this view class, we have two routes and two views, with the first leading to
the second by an HTTP redirect. Pyramid can :ref:`generate redirects
<http_redirect>` by returning a special object from a view or raising a special
exception.
In this Pyramid view, we get the URL being visited from ``request.url``.
Also, if you visited http://localhost:6543/plain?name=alice,
the name is included in the body of the response::
In this Pyramid view, we get the URL being visited from ``request.url``. Also,
if you visited http://localhost:6543/plain?name=alice, the name is included in
the body of the response:
.. code-block:: text
  URL http://localhost:6543/plain?name=alice with name: alice
Finally, we set the response's content type and body, then return the
Response.
Finally, we set the response's content type and body, then return the response.
We updated the unit and functional tests to prove that our code
does the redirection, but also handles sending and not sending
``/plain?name``.
We updated the unit and functional tests to prove that our code does the
redirection, but also handles sending and not sending ``/plain?name``.
Extra Credit
Extra credit
============
#. Could we also ``raise HTTPFound(location='/plain')`` instead of
   returning it?  If so, what's the difference?
#. Could we also ``raise HTTPFound(location='/plain')`` instead of returning
   it?  If so, what's the difference?
.. seealso:: :ref:`webob_chapter`,
   :ref:`generate redirects <http_redirect>`
docs/quick_tutorial/requirements.rst
@@ -6,40 +6,43 @@
Let's get our tutorial environment set up. Most of the set up work is in
standard Python development practices (install Python and make an isolated
environment.)
virtual environment.)
.. note::
  Pyramid encourages standard Python development practices with
  packaging tools, virtual environments, logging, and so on.  There
  are many variations, implementations, and opinions across the Python
  community.  For consistency, ease of documentation maintenance,
  and to minimize confusion, the Pyramid *documentation* has adopted
  specific conventions.
  Pyramid encourages standard Python development practices with packaging
  tools, virtual environments, logging, and so on. There are many variations,
  implementations, and opinions across the Python community.  For consistency,
  ease of documentation maintenance, and to minimize confusion, the Pyramid
  *documentation* has adopted specific conventions that are consistent with the
  :term:`Python Packaging Authority`.
This *Quick Tutorial* is based on:
* **Python 3.5**. Pyramid fully supports Python 3.3+ and Python 2.6+. This
* **Python 3.5**. Pyramid fully supports Python 3.3+ and Python 2.7+. This
  tutorial uses **Python 3.5** 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 ``venv``. For Python 2.7, you can install
  ``virtualenv``.
  Python 3.5's built-in solution :term:`venv`. For Python 2.7, you can install
  :term:`virtualenv`.
* **pip**. We use ``pip`` for package management.
* **pip**. We use :term:`pip` for package management.
* **Workspaces, projects, and packages.** Our home directory
  will contain a *tutorial workspace* with our Python virtual
  environment(s) and *Python projects* (a directory with packaging
  information and *Python packages* of working code.)
* **Workspaces, projects, and packages.** Our home directory will contain a
  *tutorial workspace* with our Python virtual environment and *Python
  projects* (a directory with packaging information and *Python packages* of
  working code.)
* **Unix commands**. Commands in this tutorial use UNIX syntax and
  paths.  Windows users should adjust commands accordingly.
* **Unix commands**. Commands in this tutorial use UNIX syntax and paths.
  Windows users should adjust commands accordingly.
.. note::
    Pyramid was one of the first web frameworks to fully support Python 3 in
    October 2011.
.. note::
    Windows commands use the plain old MSDOS shell. For PowerShell command
    syntax, see its documentation.
Steps
=====
@@ -56,27 +59,12 @@
Install Python 3
----------------
Windows and Mac OS X users can download and run an installer.
See the detailed recommendation for your operating system described under
:ref:`installing_chapter`.
Download the latest standard Python 3 release (not development release) from
`python.org <https://www.python.org/downloads/>`_.
Windows users should also install the `Python for Windows extensions
<http://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read the
``README.txt`` file at the end of the list of builds, and follow its
directions. Make sure you get the proper 32- or 64-bit build and Python
version.
Linux users can either use their package manager to install Python 3
or may `build Python 3 from source
<http://pyramid.readthedocs.org/en/master/narr/install.html#package-manager-
method>`_.
.. seealso:: See also :ref:`For Mac OS X Users <for-mac-os-x-users>`, :ref:`If
   You Don't Yet Have a Python Interpreter (UNIX)
   <if-you-don-t-yet-have-a-python-interpreter-unix>`, and :ref:`If You Don't
   Yet Have a Python Interpreter (Windows)
   <if-you-don-t-yet-have-a-python-interpreter-windows>`.
- :ref:`for-mac-os-x-users`
- :ref:`if-you-don-t-yet-have-a-python-interpreter-unix`
- :ref:`if-you-don-t-yet-have-a-python-interpreter-windows`
.. _create-a-project-directory-structure:
@@ -84,11 +72,10 @@
Create a project directory structure
------------------------------------
We will arrive at a directory structure of
``workspace->project->package``, with our workspace named
``quick_tutorial``. The following tree diagram shows how this will be
structured and where our virtual environment will reside as we proceed through
the tutorial:
We will arrive at a directory structure of ``workspace -> project -> package``,
where our workspace is named ``quick_tutorial``. The following tree diagram
shows how this will be structured, and where our :term:`virtual environment`
will reside as we proceed through the tutorial:
.. code-block:: text
@@ -113,49 +100,47 @@
For Windows:
.. code-block:: ps1con
.. code-block:: doscon
    # Windows
    c:\> cd \
    c:\> mkdir projects\quick_tutorial
    c:\> cd projects\quick_tutorial
In the above figure, your user home directory is represented by ``~``.  In
your home directory, all of your projects are in the ``projects`` directory.
This is a general convention not specific to Pyramid that many developers use.
Windows users will do well to use ``c:\`` as the location for ``projects`` in
order to avoid spaces in any of the path names.
In the above figure, your user home directory is represented by ``~``. In your
home directory, all of your projects are in the ``projects`` directory. This is
a general convention not specific to Pyramid that many developers use. Windows
users will do well to use ``c:\`` as the location for ``projects`` in order to
avoid spaces in any of the path names.
Next within ``projects`` is your workspace directory, here named
``quick_tutorial``. A workspace is a common term used by integrated
development environments (IDE) like PyCharm and PyDev that stores
isolated Python environments (virtual environments) and specific project files
and repositories.
development environments (IDE), like PyCharm and PyDev, where virtual
environments, specific project files, and repositories are stored.
.. _set-an-environment-variable:
Set an Environment Variable
Set an environment variable
---------------------------
This tutorial will refer frequently to the location of the virtual
environment. We set an environment variable to save typing later.
This tutorial will refer frequently to the location of the :term:`virtual
environment`. We set an environment variable to save typing later.
.. code-block:: bash
    # Mac and Linux
    $ export VENV=~/projects/quick_tutorial/env
.. code-block:: ps1con
.. code-block:: doscon
    # Windows
    # TODO: This command does not work
    c:\> set VENV=c:\projects\quick_tutorial\env
.. _create-a-virtual-environment:
Create a Virtual Environment
Create a virtual environment
----------------------------
``venv`` is a tool to create isolated Python 3 environments, each with its own
@@ -168,7 +153,7 @@
    # Mac and Linux
    $ python3 -m venv $VENV
.. code-block:: ps1con
.. code-block:: doscon
    # Windows
    c:\> c:\Python35\python3 -m venv %VENV%
@@ -177,13 +162,31 @@
   2's `virtualenv <https://virtualenv.pypa.io/en/latest/>`_ package.
Update packaging tools in the virtual environment
-------------------------------------------------
It's always a good idea to update to the very latest version of packaging tools
because the installed Python bundles only the version that was available at the
time of its release.
.. code-block:: bash
    # Mac and Linux
    $VENV/bin/pip install --upgrade pip setuptools
.. code-block:: doscon
    # Windows
    c:\> %VENV%\Scripts\pip install --upgrade pip setuptools
.. _install-pyramid:
Install Pyramid
---------------
We have our Python standard prerequisites out of the way. The Pyramid
part is pretty easy:
part is pretty easy.
.. parsed-literal::
@@ -195,18 +198,17 @@
Our Python virtual environment now has the Pyramid software available.
You can optionally install some of the extra Python packages used
during this tutorial:
You can optionally install some of the extra Python packages used in this
tutorial.
.. code-block:: bash
    # Mac and Linux
    $ $VENV/bin/pip install nose webtest deform sqlalchemy \
       pyramid_chameleon pyramid_debugtoolbar waitress \
       pyramid_tm zope.sqlalchemy
    $ $VENV/bin/pip install webtest pytest pytest-cov deform sqlalchemy \
      pyramid_chameleon pyramid_debugtoolbar pyramid_jinja2 waitress \
      pyramid_tm zope.sqlalchemy
.. code-block:: ps1con
.. code-block:: doscon
    # Windows
    c:\> %VENV%\Scripts\pip install nose webtest deform sqlalchemy pyramid_chameleon pyramid_debugtoolbar waitress pyramid_tm zope.sqlalchemy
    c:\> %VENV%\Scripts\pip install webtest deform sqlalchemy pyramid_chameleon pyramid_debugtoolbar pyramid_jinja2 waitress pyramid_tm zope.sqlalchemy
docs/quick_tutorial/routing.rst
@@ -4,22 +4,23 @@
11: Dispatching URLs To Views With Routing
==========================================
Routing matches incoming URL patterns to view code. Pyramid's routing
has a number of useful features.
Routing matches incoming URL patterns to view code. Pyramid's routing has a
number of useful features.
Background
==========
Writing web applications usually means sophisticated URL design. We
just saw some Pyramid machinery for requests and views. Let's look at
features that help in routing.
Writing web applications usually means sophisticated URL design. We just saw
some Pyramid machinery for requests and views. Let's look at features that help
in routing.
Previously we saw the basics of routing URLs to views in Pyramid.
- Your project's "setup" code registers a route name to be used when
  matching part of the URL
- Your project's "setup" code registers a route name to be used when matching
  part of the URL
- Elsewhere, a view is configured to be called for that route name
- Elsewhere a view is configured to be called for that route name.
.. note::
@@ -33,12 +34,14 @@
    <http://static.repoze.org/casts/videotags.html>`_ if you're interested in
    doing so.
Objectives
==========
- Define a route that extracts part of the URL into a Python dictionary
- Define a route that extracts part of the URL into a Python dictionary.
- Use that dictionary data in a view
- Use that dictionary data in a view.
Steps
=====
@@ -76,7 +79,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/$VENV/bin/py.test tutorial/tests.py -q
    ..
    2 passed in 0.39 seconds
#. Run your Pyramid application with:
@@ -85,6 +90,7 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/howdy/amy/smith in your browser.
Analysis
========
@@ -95,27 +101,24 @@
    config.add_route('hello', '/howdy/{first}/{last}')
With this we tell the :term:`configurator` that our URL has
a "replacement pattern".  With this, URLs such as ``/howdy/amy/smith``
will assign ``amy`` to ``first`` and ``smith`` to ``last``. We can then
use this data in our view:
With this we tell the :term:`configurator` that our URL has a "replacement
pattern". With this, URLs such as ``/howdy/amy/smith`` will assign ``amy`` to
``first`` and ``smith`` to ``last``. We can then use this data in our view:
.. code-block:: python
    self.request.matchdict['first']
    self.request.matchdict['last']
``request.matchdict`` contains values from the URL that match the
"replacement patterns" (the curly braces) in the route declaration.
This information can then be used anywhere in Pyramid that has access
to the request.
``request.matchdict`` contains values from the URL that match the "replacement
patterns" (the curly braces) in the route declaration. This information can
then be used anywhere in Pyramid that has access to the request.
Extra Credit
Extra credit
============
#. What happens if you to go the URL
   http://localhost:6543/howdy? Is this the result that you
   expected?
#. What happens if you to go the URL http://localhost:6543/howdy? Is this the
   result that you expected?
.. seealso:: `Weird Stuff You Can Do With URL
   Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_
.. seealso:: `Weird Stuff You Can Do With URL Dispatch
   <http://www.plope.com/weird_pyramid_urldispatch>`_
docs/quick_tutorial/scaffolds.rst
@@ -4,29 +4,30 @@
Prelude: Quick Project Startup with Scaffolds
=============================================
To ease the process of getting started, Pyramid provides *scaffolds*
that generate sample projects from templates in Pyramid and Pyramid
add-ons.
To ease the process of getting started, Pyramid provides *scaffolds* that
generate sample projects from templates in Pyramid and Pyramid add-ons.
Background
==========
We're going to cover a lot in this tutorial, focusing on one topic at a
time and writing everything from scratch. As a warm up, though,
it sure would be nice to see some pixels on a screen.
We're going to cover a lot in this tutorial, focusing on one topic at a time
and writing everything from scratch. As a warm up, though, it sure would be
nice to see some pixels on a screen.
Like other web development frameworks, Pyramid provides a number of
"scaffolds" that generate working Python, template, and CSS code for
sample applications. In this step we'll use a built-in scaffold to let
us preview a Pyramid application, before starting from scratch on Step 1.
Like other web development frameworks, Pyramid provides a number of "scaffolds"
that generate working Python, template, and CSS code for sample applications.
In this step we'll use a built-in scaffold to let us preview a Pyramid
application, before starting from scratch on Step 1.
Objectives
==========
- Use Pyramid's ``pcreate`` command to list scaffolds and make a new
  project
- Use Pyramid's ``pcreate`` command to list scaffolds and make a new project.
- Start up a Pyramid application and visit it in a web browser
- Start up a Pyramid application and visit it in a web browser.
Steps
=====
@@ -55,8 +56,8 @@
        $ cd scaffolds
        $ $VENV/bin/pip install -e .
#. Start up the application by pointing Pyramid's ``pserve`` command at
   the project's (generated) configuration file:
#. Start up the application by pointing Pyramid's ``pserve`` command at the
   project's (generated) configuration file:
    .. code-block:: bash
@@ -75,13 +76,12 @@
Analysis
========
Rather than starting from scratch, ``pcreate`` can make getting a
Python project containing a Pyramid application a quick matter.
Pyramid ships with a few scaffolds. But installing a Pyramid add-on can
give you new scaffolds from that add-on.
Rather than starting from scratch, ``pcreate`` can make getting a Python
project containing a Pyramid application a quick matter. Pyramid ships with a
few scaffolds. But installing a Pyramid add-on can give you new scaffolds from
that add-on.
``pserve`` is Pyramid's application runner, separating operational
details from your code. When you install Pyramid, a small command
program called ``pserve`` is written to your ``bin`` directory. This
program is an executable Python module. It is passed a configuration
file (in this case, ``development.ini``.)
``pserve`` is Pyramid's application runner, separating operational details from
your code. When you install Pyramid, a small command program called ``pserve``
is written to your ``bin`` directory. This program is an executable Python
module. It is passed a configuration file (in this case, ``development.ini``).
docs/quick_tutorial/sessions.rst
@@ -6,25 +6,28 @@
Store and retrieve non-permanent data in Pyramid sessions.
Background
==========
When people use your web application, they frequently perform a task
that requires semi-permanent data to be saved. For example, a shopping
cart. This is called a :term:`session`.
When people use your web application, they frequently perform a task that
requires semi-permanent data to be saved. For example, a shopping cart. This is
called a :term:`session`.
Pyramid has basic built-in support for sessions.  Third party packages such as
``pyramid_redis_sessions`` provide richer session support.  Or you can create
your own custom sessioning engine.  Let's take a look at the
:doc:`built-in sessioning support <../narr/sessions>`.
`pyramid_redis_sessions
<https://github.com/ericrasmussen/pyramid_redis_sessions>`_ provide richer
session support. Or you can create your own custom sessioning engine. Let's
take a look at the :doc:`built-in sessioning support <../narr/sessions>`.
Objectives
==========
- Make a session factory using a built-in, simple Pyramid sessioning
  system
- Make a session factory using a built-in, simple Pyramid sessioning system.
- Change our code to use a session
- Change our code to use a session.
Steps
=====
@@ -36,14 +39,13 @@
    $ cd ..; cp -r view_classes sessions; cd sessions
    $ $VENV/bin/pip install -e .
#. Our ``sessions/tutorial/__init__.py`` needs a choice of session
   factory to get registered with the :term:`configurator`:
#. Our ``sessions/tutorial/__init__.py`` needs a choice of session factory to
   get registered with the :term:`configurator`:
   .. literalinclude:: sessions/tutorial/__init__.py
    :linenos:
#. Our views in ``sessions/tutorial/views.py`` can now use
   ``request.session``:
#. Our views in ``sessions/tutorial/views.py`` can now use ``request.session``:
   .. literalinclude:: sessions/tutorial/views.py
    :linenos:
@@ -58,7 +60,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.42 seconds
#. Run your Pyramid application with:
@@ -66,33 +70,33 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ and http://localhost:6543/howdy
   in your browser. As you reload and switch between those URLs, note
   that the counter increases and is *not* specific to the URL.
#. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser.
   As you reload and switch between those URLs, note that the counter increases
   and is *not* specific to the URL.
#. Restart the application and revisit the page. Note that counter
   still increases from where it left off.
#. Restart the application and revisit the page. Note that counter still
   increases from where it left off.
Analysis
========
Pyramid's :term:`request` object now has a ``session`` attribute
that we can use in our view code. It acts like a dictionary.
Pyramid's :term:`request` object now has a ``session`` attribute that we can
use in our view code. It acts like a dictionary.
Since all the views are using the same counter, we made the counter a
Python property at the view class level. With this, each reload will
increase the counter displayed in our template.
Since all the views are using the same counter, we made the counter a Python
property at the view class level. With this, each reload will increase the
counter displayed in our template.
In web development, "flash messages" are notes for the user that need
to appear on a screen after a future web request. For example,
when you add an item using a form ``POST``, the site usually issues a
second HTTP Redirect web request to view the new item. You might want a
message to appear after that second web request saying "Your item was
added." You can't just return it in the web response for the POST,
as it will be tossed out during the second web request.
In web development, "flash messages" are notes for the user that need to appear
on a screen after a future web request. For example, when you add an item using
a form ``POST``, the site usually issues a second HTTP Redirect web request to
view the new item. You might want a message to appear after that second web
request saying "Your item was added." You can't just return it in the web
response for the POST, as it will be tossed out during the second web request.
Flash messages are a technique where messages can be stored between
requests, using sessions, then removed when they finally get displayed.
Flash messages are a technique where messages can be stored between requests,
using sessions, then removed when they finally get displayed.
.. seealso::
   :ref:`sessions_chapter`,
docs/quick_tutorial/static_assets.rst
@@ -4,16 +4,17 @@
13: CSS/JS/Images Files With Static Assets
==========================================
Of course the Web is more than just markup. You need static assets:
CSS, JS, and images. Let's point our web app at a directory where
Pyramid will serve some static assets.
Of course the Web is more than just markup. You need static assets: CSS, JS,
and images. Let's point our web app at a directory where Pyramid will serve
some static assets.
Objectives
==========
- Publish a directory of static assets at a URL
- Publish a directory of static assets at a URL.
- Use Pyramid to help generate URLs to files in that directory
- Use Pyramid to help generate URLs to files in that directory.
Steps
=====
@@ -37,8 +38,7 @@
   .. literalinclude:: static_assets/tutorial/home.pt
    :language: html
#. Add a CSS file at
   ``static_assets/tutorial/static/app.css``:
#. Add a CSS file at ``static_assets/tutorial/static/app.css``:
   .. literalinclude:: static_assets/tutorial/static/app.css
    :language: css
@@ -47,7 +47,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/$VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.50 seconds
#. Run your Pyramid application with:
@@ -57,30 +59,31 @@
#. Open http://localhost:6543/ in your browser and note the new font.
Analysis
========
We changed our WSGI application to map requests under
http://localhost:6543/static/ to files and directories inside a
``static`` directory inside our ``tutorial`` package. This directory
contained ``app.css``.
http://localhost:6543/static/ to files and directories inside a ``static``
directory inside our ``tutorial`` package. This directory contained
``app.css``.
We linked to the CSS in our template. We could have hard-coded this
link to ``/static/app.css``. But what if the site is later moved under
``/somesite/static/``? Or perhaps the web developer changes the
arrangement on disk? Pyramid gives a helper that provides flexibility
on URL generation:
We linked to the CSS in our template. We could have hard-coded this link to
``/static/app.css``. But what if the site is later moved under
``/somesite/static/``? Or perhaps the web developer changes the arrangement on
disk? Pyramid gives a helper that provides flexibility on URL generation:
.. code-block:: html
  ${request.static_url('tutorial:static/app.css')}
This matches the ``path='tutorial:static'`` in our
``config.add_static_view`` registration. By using ``request.static_url``
to generate the full URL to the static assets, you both ensure you stay
in sync with the configuration and gain refactoring flexibility later.
This matches the ``path='tutorial:static'`` in our ``config.add_static_view``
registration. By using ``request.static_url`` to generate the full URL to the
static assets, you both ensure you stay in sync with the configuration and gain
refactoring flexibility later.
Extra Credit
Extra credit
============
#. There is also a ``request.static_path`` API.  How does this differ from 
docs/quick_tutorial/templating.rst
@@ -4,50 +4,53 @@
08: HTML Generation With Templating
===================================
Most web frameworks don't embed HTML in programming code. Instead,
they pass data into a templating system. In this step we look at the
basics of using HTML templates in Pyramid.
Most web frameworks don't embed HTML in programming code. Instead, they pass
data into a templating system. In this step we look at the basics of using HTML
templates in Pyramid.
Background
==========
Ouch. We have been making our own ``Response`` and filling the response
body with HTML. You usually won't embed an HTML string directly in
Python, but instead, will use a templating language.
Ouch. We have been making our own ``Response`` and filling the response body
with HTML. You usually won't embed an HTML string directly in Python, but
instead will use a templating language.
Pyramid doesn't mandate a particular database system, form library,
etc. It encourages replaceability. This applies equally to templating,
which is fortunate: developers have strong views about template
languages. As of Pyramid 1.5a2, Pyramid doesn't even bundle a template
language!
Pyramid doesn't mandate a particular database system, form library, and so on.
It encourages replaceability. This applies equally to templating, which is
fortunate: developers have strong views about template languages. As of
Pyramid 1.5a2, Pyramid doesn't even bundle a template language!
It does, however, have strong ties to Jinja2, Mako, and Chameleon. In
this step we see how to add ``pyramid_chameleon`` to your project,
then change your views to use templating.
It does, however, have strong ties to Jinja2, Mako, and Chameleon. In this step
we see how to add `pyramid_chameleon
<https://github.com/Pylons/pyramid_chameleon>`_ to your project, then change
your views to use templating.
Objectives
==========
- Enable the ``pyramid_chameleon`` Pyramid add-on
- Enable the ``pyramid_chameleon`` Pyramid add-on.
- Generate HTML from template files
- Generate HTML from template files.
- Connect the templates as "renderers" for view code
- Connect the templates as "renderers" for view code.
- Change the view code to simply return data
- Change the view code to simply return data.
Steps
=====
#. Let's begin by using the previous package as a starting point for a
   new project:
#. Let's begin by using the previous package as a starting point for a new
   project:
   .. code-block:: bash
    $ cd ..; cp -r views templating; cd templating
#. This step depends on ``pyramid_chameleon``, so add it as a dependency
   in ``templating/setup.py``:
#. This step depends on ``pyramid_chameleon``, so add it as a dependency in
   ``templating/setup.py``:
   .. literalinclude:: templating/setup.py
    :linenos:
@@ -58,8 +61,8 @@
    $ $VENV/bin/pip install -e .
#. We need to connect ``pyramid_chameleon`` as a renderer by making a
   call in the setup of ``templating/tutorial/__init__.py``:
#. We need to connect ``pyramid_chameleon`` as a renderer by making a call in
   the setup of ``templating/tutorial/__init__.py``:
   .. literalinclude:: templating/tutorial/__init__.py
    :linenos:
@@ -74,14 +77,13 @@
   .. literalinclude:: templating/tutorial/home.pt
    :language: html
#. For convenience, change ``templating/development.ini`` to reload
   templates automatically with ``pyramid.reload_templates``:
#. For convenience, change ``templating/development.ini`` to reload templates
   automatically with ``pyramid.reload_templates``:
   .. literalinclude:: templating/development.ini
    :language: ini
#. Our unit tests in ``templating/tutorial/tests.py`` can focus on
   data:
#. Our unit tests in ``templating/tutorial/tests.py`` can focus on data:
   .. literalinclude:: templating/tutorial/tests.py
    :linenos:
@@ -90,13 +92,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    .
    ----------------------------------------------------------------------
    Ran 4 tests in 0.141s
    OK
    $ $VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.46 seconds
#. Run your Pyramid application with:
@@ -104,20 +102,19 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ and http://localhost:6543/howdy
   in your browser.
#. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser.
Analysis
========
Ahh, that looks better. We have a view that is focused on Python code.
Our ``@view_config`` decorator specifies a :term:`renderer` that points
to our template file. Our view then simply returns data which is then
supplied to our template. Note that we used the same template for both
views.
Ahh, that looks better. We have a view that is focused on Python code. Our
``@view_config`` decorator specifies a :term:`renderer` that points to our
template file. Our view then simply returns data which is then supplied to our
template. Note that we used the same template for both views.
Note the effect on testing. We can focus on having a data-oriented
contract with our view code.
Note the effect on testing. We can focus on having a data-oriented contract
with our view code.
.. seealso:: :ref:`templates_chapter`, :ref:`debugging_templates`, and
   :ref:`available_template_system_bindings`.
docs/quick_tutorial/tutorial_approach.rst
@@ -2,44 +2,46 @@
Tutorial Approach
=================
This tutorial uses conventions to keep the introduction focused and
concise. Details, references, and deeper discussions are mentioned in
"See also" notes.
This tutorial uses conventions to keep the introduction focused and concise.
Details, references, and deeper discussions are mentioned in "See also" notes.
.. seealso:: This is an example "See also" note.
This "Getting Started" tutorial is broken into independent steps,
starting with the smallest possible "single file WSGI app" example.
Each of these steps introduce a topic and a very small set of concepts
via working code. The steps each correspond to a directory in this
repo, where each step/topic/directory is a Python package.
This "Getting Started" tutorial is broken into independent steps, starting with
the smallest possible "single file WSGI app" example. Each of these steps
introduce a topic and a very small set of concepts via working code. The steps
each correspond to a directory in this repo, where each step/topic/directory is
a Python package.
To successfully run each step::
To successfully run each step:
  $ cd request_response
  $ $VENV/bin/pip install -e .
.. code-block:: bash
...and repeat for each step you would like to work on. In most cases we
will start with the results of an earlier step.
    $ cd request_response
    $ $VENV/bin/pip install -e .
Directory Tree
...and repeat for each step you would like to work on. In most cases we will
start with the results of an earlier step.
Directory tree
==============
As we develop our tutorial our directory tree will resemble the
structure below::
As we develop our tutorial, our directory tree will resemble the structure
below:
  quicktutorial/
    request_response/
      development.ini
      setup.py
      tutorial/
        __init__.py
        home.pt
        tests.py
        views.py
.. code-block:: text
Each of the first-level directories (e.g. ``request_response``) is a
*Python project* (except, as noted, the ``hello_world`` step.) The
``tutorial`` directory is a *Python package*. At the end of each step,
we copy a previous directory into a new directory to use as a starting
point.
    quick_tutorial
        â”œâ”€â”€ env
        â””── request_response
            â”œâ”€â”€ tutorial
            â”‚   â”œâ”€â”€ __init__.py
            â”‚   â”œâ”€â”€ tests.py
            â”‚   â””── views.py
            â”œâ”€â”€ development.ini
            â””── setup.py
Each of the first-level directories (e.g., ``request_response``) is a *Python
project* (except as noted for the ``hello_world`` step). The ``tutorial``
directory is a *Python package*. At the end of each step, we copy a previous
directory into a new directory to use as a starting point.
docs/quick_tutorial/unit_testing.rst
@@ -1,55 +1,56 @@
.. _qtut_unit_testing:
===========================
05: Unit Tests and ``nose``
===========================
=============================
05: Unit Tests and ``pytest``
=============================
Provide unit testing for our project's Python code.
Background
==========
As the mantra says, "Untested code is broken code." The Python
community has had a long culture of writing test scripts which ensure
that your code works correctly as you write it and maintain it in the
future. Pyramid has always had a deep commitment to testing,
with 100% test coverage from the earliest pre-releases.
As the mantra says, "Untested code is broken code." The Python community has
had a long culture of writing test scripts which ensure that your code works
correctly as you write it and maintain it in the future. Pyramid has always had
a deep commitment to testing, with 100% test coverage from the earliest
pre-releases.
Python includes a
:ref:`unit testing framework <python:unittest-minimal-example>` in its
standard library. Over the years a number of Python projects, such as
`nose <https://pypi.python.org/pypi/nose/>`_, have extended this
framework with alternative test runners that provide more convenience
and functionality. The Pyramid developers use ``nose``, which we'll thus
use in this tutorial.
Python includes a :ref:`unit testing framework
<python:unittest-minimal-example>` in its standard library. Over the years a
number of Python projects, such as :ref:`pytest <pytest:features>`, have
extended this framework with alternative test runners that provide more
convenience and functionality. The Pyramid developers use ``pytest``, which
we'll use in this tutorial.
Don't worry, this tutorial won't be pedantic about "test-driven
development" (TDD). We'll do just enough to ensure that, in each step,
we haven't majorly broken the code. As you're writing your code you
might find this more convenient than changing to your browser
constantly and clicking reload.
Don't worry, this tutorial won't be pedantic about "test-driven development"
(TDD). We'll do just enough to ensure that, in each step, we haven't majorly
broken the code. As you're writing your code, you might find this more
convenient than changing to your browser constantly and clicking reload.
We'll also leave discussion of
`coverage <https://pypi.python.org/pypi/coverage>`_ for another section.
We'll also leave discussion of `pytest-cov
<http://pytest-cov.readthedocs.org/en/latest/>`_ for another section.
Objectives
==========
- Write unit tests that ensure the quality of our code
- Write unit tests that ensure the quality of our code.
- Install a Python package (``nose``) which helps in our testing
- Install a Python package (``pytest``) which helps in our testing.
Steps
=====
#. First we copy the results of the previous step, as well as install
   the ``nose`` package:
#. First we copy the results of the previous step, as well as install the
   ``pytest`` package:
   .. code-block:: bash
    $ cd ..; cp -r debugtoolbar unit_testing; cd unit_testing
    $ $VENV/bin/pip install -e .
    $ $VENV/bin/pip install nose
    $ $VENV/bin/pip install pytest
#. Now we write a simple unit test in ``unit_testing/tutorial/tests.py``:
@@ -61,54 +62,51 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    $ $VENV/bin/py.test tutorial/tests.py -q
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.141s
    1 passed in 0.14 seconds
    OK
Analysis
========
Our ``tests.py`` imports the Python standard unit testing framework. To
make writing Pyramid-oriented tests more convenient, Pyramid supplies
some ``pyramid.testing`` helpers which we use in the test setup and
teardown. Our one test imports the view, makes a dummy request, and sees
if the view returns what we expected.
Our ``tests.py`` imports the Python standard unit testing framework. To make
writing Pyramid-oriented tests more convenient, Pyramid supplies some
``pyramid.testing`` helpers which we use in the test setup and teardown. Our
one test imports the view, makes a dummy request, and sees if the view returns
what we expect.
The ``tests.TutorialViewTests.test_hello_world`` test is a small
example of a unit test. First, we import the view inside each test. Why
not import at the top, like in normal Python code? Because imports can
cause effects that break a test. We'd like our tests to be in *units*,
hence the name *unit* testing. Each test should isolate itself to the
correct degree.
The ``tests.TutorialViewTests.test_hello_world`` test is a small example of a
unit test. First, we import the view inside each test. Why not import at the
top, like in normal Python code? Because imports can cause effects that break a
test. We'd like our tests to be in *units*, hence the name *unit* testing. Each
test should isolate itself to the correct degree.
Our test then makes a fake incoming web request, then calls our Pyramid
view. We test the HTTP status code on the response to make sure it
matches our expectations.
Our test then makes a fake incoming web request, then calls our Pyramid view.
We test the HTTP status code on the response to make sure it matches our
expectations.
Note that our use of ``pyramid.testing.setUp()`` and
``pyramid.testing.tearDown()`` aren't actually necessary here; they are only
necessary when your test needs to make use of the ``config`` object (it's a
Configurator) to add stuff to the configuration state before calling the view.
Extra Credit
============
#. Change the test to assert that the response status code should be
   ``404`` (meaning, not found.) Run ``nosetests`` again. Read the
   error report and see if you can decipher what it is telling you.
#. Change the test to assert that the response status code should be ``404``
   (meaning, not found). Run ``py.test`` again. Read the error report and see
   if you can decipher what it is telling you.
#. As a more realistic example, put the ``tests.py`` back as you found
   it and put an error in your view, such as a reference to a
   non-existing variable. Run the tests and see how this is more
   convenient than reloading your browser and going back to your code.
#. As a more realistic example, put the ``tests.py`` back as you found it, and
   put an error in your view, such as a reference to a non-existing variable.
   Run the tests and see how this is more convenient than reloading your
   browser and going back to your code.
#. Finally, for the most realistic test, read about Pyramid ``Response``
   objects and see how to change the response code. Run the tests and
   see how testing confirms the "contract" that your code claims to
   support.
   objects and see how to change the response code. Run the tests and see how
   testing confirms the "contract" that your code claims to support.
#. How could we add a unit test assertion to test the HTML value of the
   response body?
docs/quick_tutorial/view_classes.rst
@@ -4,8 +4,9 @@
09: Organizing Views With View Classes
======================================
Change our view functions to be methods on a view class,
then move some declarations to the class level.
Change our view functions to be methods on a view class, then move some
declarations to the class level.
Background
==========
@@ -15,26 +16,26 @@
on the same data, or be a REST API that handles multiple operations. Grouping
these views together as a :ref:`view class <class_as_view>` makes sense:
- Group views
- Group views.
- Centralize some repetitive defaults
- Centralize some repetitive defaults.
- Share some state and helpers
- Share some state and helpers.
In this step we just do the absolute minimum to convert the existing
views to a view class. In a later tutorial step we'll examine view
classes in depth.
In this step we just do the absolute minimum to convert the existing views to a
view class. In a later tutorial step, we'll examine view classes in depth.
Objectives
==========
- Group related views into a view class
- Group related views into a view class.
- Centralize configuration with class-level ``@view_defaults``
- Centralize configuration with class-level ``@view_defaults``.
Steps
=====
#. First we copy the results of the previous step:
@@ -43,15 +44,15 @@
    $ cd ..; cp -r templating view_classes; cd view_classes
    $ $VENV/bin/pip install -e .
#. Our ``view_classes/tutorial/views.py`` now has a view class with
   our two views:
#. Our ``view_classes/tutorial/views.py`` now has a view class with our two
   views:
   .. literalinclude:: view_classes/tutorial/views.py
    :linenos:
#. Our unit tests in ``view_classes/tutorial/tests.py`` don't run,
   so let's modify them to import the view class and make an instance
   before getting a response:
#. Our unit tests in ``view_classes/tutorial/tests.py`` don't run, so let's
   modify them to import the view class, and make an instance before getting a
   response:
   .. literalinclude:: view_classes/tutorial/tests.py
    :linenos:
@@ -61,12 +62,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    .
    ----------------------------------------------------------------------
    Ran 4 tests in 0.141s
    OK
    $ $VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.34 seconds
#. Run your Pyramid application with:
@@ -74,24 +72,23 @@
    $ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ and http://localhost:6543/howdy
   in your browser.
#. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser.
Analysis
========
To ease the transition to view classes, we didn't introduce any new
functionality. We simply changed the view functions to methods on a
view class, then updated the tests.
functionality. We simply changed the view functions to methods on a view class,
then updated the tests.
In our ``TutorialViews`` view class you can see that our two view
classes are logically grouped together as methods on a common class.
Since the two views shared the same template, we could move that to a
``@view_defaults`` decorator at the class level.
In our ``TutorialViews`` view class, you can see that our two view classes are
logically grouped together as methods on a common class. Since the two views
shared the same template, we could move that to a ``@view_defaults`` decorator
at the class level.
The tests needed to change. Obviously we needed to import the view
class. But you can also see the pattern in the tests of instantiating
the view class with the dummy request first, then calling the view
method being tested.
The tests needed to change. Obviously we needed to import the view class. But
you can also see the pattern in the tests of instantiating the view class with
the dummy request first, then calling the view method being tested.
.. seealso:: :ref:`class_as_view`
docs/quick_tutorial/views.rst
@@ -6,12 +6,12 @@
Organize a views module with decorators and multiple views.
Background
==========
For the examples so far, the ``hello_world`` function is a "view". In
Pyramid, views are the primary way to accept web requests and return
responses.
For the examples so far, the ``hello_world`` function is a "view". In Pyramid,
views are the primary way to accept web requests and return responses.
So far our examples place everything in one file:
@@ -23,22 +23,24 @@
- The WSGI application launcher
Let's move the views out to their own ``views.py`` module and change
our startup code to scan that module, looking for decorators that setup
the views. Let's also add a second view and update our tests.
Let's move the views out to their own ``views.py`` module and change our
startup code to scan that module, looking for decorators that set up the views.
Let's also add a second view and update our tests.
Objectives
==========
- Views in a module that is scanned by the configurator
- Move views into a module that is scanned by the configurator.
- Decorators that do declarative configuration
- Create decorators that do declarative configuration.
Steps
=====
#. Let's begin by using the previous package as a starting point for a
   new distribution, then making it active:
#. Let's begin by using the previous package as a starting point for a new
   distribution, then making it active:
   .. code-block:: bash
@@ -66,12 +68,9 @@
   .. code-block:: bash
    $ $VENV/bin/nosetests tutorial
    .
    ----------------------------------------------------------------------
    Ran 4 tests in 0.141s
    OK
    $ $VENV/bin/py.test tutorial/tests.py -q
    ....
    4 passed in 0.28 seconds
#. Run your Pyramid application with:
@@ -82,41 +81,41 @@
#. Open http://localhost:6543/ and http://localhost:6543/howdy
   in your browser.
Analysis
========
We added some more URLs, but we also removed the view code from the
application startup code in ``tutorial/__init__.py``.
Our views, and their view registrations (via decorators) are now in a
module ``views.py`` which is scanned via ``config.scan('.views')``.
We added some more URLs, but we also removed the view code from the application
startup code in ``tutorial/__init__.py``. Our views, and their view
registrations (via decorators) are now in a module ``views.py``, which is
scanned via ``config.scan('.views')``.
We have 2 views, each leading to the other. If you start at
http://localhost:6543/, you get a response with a link to the next
view. The ``hello`` view (available at the URL ``/howdy``) has a link
back to the first view.
We have two views, each leading to the other. If you start at
http://localhost:6543/, you get a response with a link to the next view. The
``hello`` view (available at the URL ``/howdy``) has a link back to the first
view.
This step also shows that the name appearing in the URL,
the name of the "route" that maps a URL to a view,
and the name of the view, can all be different. More on routes later.
This step also shows that the name appearing in the URL, the name of the
"route" that maps a URL to a view, and the name of the view, can all be
different. More on routes later.
Earlier we saw ``config.add_view`` as one way to configure a view. This
section introduces ``@view_config``. Pyramid's configuration supports
:term:`imperative configuration`, such as the
``config.add_view`` in the previous example. You can also use
:term:`declarative configuration`, in which a Python
:term:`python:decorator`
is placed on the line above the view. Both approaches result in the
same final configuration, thus usually, it is simply a matter of taste.
Earlier we saw ``config.add_view`` as one way to configure a view. This section
introduces ``@view_config``. Pyramid's configuration supports :term:`imperative
configuration`, such as the ``config.add_view`` in the previous example. You
can also use :term:`declarative configuration`, in which a Python
:term:`python:decorator` is placed on the line above the view. Both approaches
result in the same final configuration, thus usually, it is simply a matter of
taste.
Extra Credit
Extra credit
============
#. What does the dot in ``.views`` signify?
#. Why might ``assertIn`` be a better choice in testing the text in
   responses than ``assertEqual``?
#. Why might ``assertIn`` be a better choice in testing the text in responses
   than ``assertEqual``?
.. seealso:: :ref:`views_chapter`,
   :ref:`view_config_chapter`, and
   :ref:`debugging_view_configuration`