summaryrefslogtreecommitdiff
path: root/docs/quick_tutorial
diff options
context:
space:
mode:
Diffstat (limited to 'docs/quick_tutorial')
-rw-r--r--docs/quick_tutorial/authentication.rst133
-rw-r--r--docs/quick_tutorial/authentication/development.ini36
-rw-r--r--docs/quick_tutorial/authentication/setup.py34
-rw-r--r--docs/quick_tutorial/authentication/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/authentication/tutorial/login.pt4
-rw-r--r--docs/quick_tutorial/authentication/tutorial/security.py16
-rw-r--r--docs/quick_tutorial/authentication/tutorial/views.py8
-rw-r--r--docs/quick_tutorial/authorization.rst101
-rw-r--r--docs/quick_tutorial/authorization/development.ini36
-rw-r--r--docs/quick_tutorial/authorization/setup.py34
-rw-r--r--docs/quick_tutorial/authorization/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/authorization/tutorial/login.pt4
-rw-r--r--docs/quick_tutorial/authorization/tutorial/security.py16
-rw-r--r--docs/quick_tutorial/authorization/tutorial/views.py8
-rw-r--r--docs/quick_tutorial/conf.py281
-rw-r--r--docs/quick_tutorial/cookiecutters.rst92
-rw-r--r--docs/quick_tutorial/cookiecutters/.coveragerc3
-rw-r--r--docs/quick_tutorial/cookiecutters/.gitignore21
-rw-r--r--docs/quick_tutorial/cookiecutters/CHANGES.txt4
-rw-r--r--docs/quick_tutorial/cookiecutters/MANIFEST.in2
-rw-r--r--docs/quick_tutorial/cookiecutters/README.txt29
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/__init__.py11
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/routes.py3
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid-16x16.pngbin0 -> 1319 bytes
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid.pngbin0 -> 12901 bytes
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/static/theme.css154
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/templates/404.jinja28
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja264
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/templates/mytemplate.jinja28
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/tests.py29
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/views/__init__.py0
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/views/default.py6
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/views/notfound.py7
-rw-r--r--docs/quick_tutorial/cookiecutters/development.ini (renamed from docs/quick_tutorial/scaffolds/development.ini)19
-rw-r--r--docs/quick_tutorial/cookiecutters/production.ini (renamed from docs/quick_tutorial/scaffolds/production.ini)17
-rw-r--r--docs/quick_tutorial/cookiecutters/pytest.ini3
-rw-r--r--docs/quick_tutorial/cookiecutters/setup.py52
-rw-r--r--docs/quick_tutorial/databases.rst224
-rw-r--r--docs/quick_tutorial/databases/development.ini17
-rw-r--r--docs/quick_tutorial/databases/setup.py42
-rw-r--r--docs/quick_tutorial/databases/sqltutorial.sqlitebin12288 -> 0 bytes
-rw-r--r--docs/quick_tutorial/databases/tutorial/tests.py6
-rw-r--r--docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt15
-rw-r--r--docs/quick_tutorial/debugtoolbar.rst156
-rw-r--r--docs/quick_tutorial/debugtoolbar/development.ini36
-rw-r--r--docs/quick_tutorial/debugtoolbar/setup.py29
-rw-r--r--docs/quick_tutorial/forms.rst183
-rw-r--r--docs/quick_tutorial/forms/development.ini36
-rw-r--r--docs/quick_tutorial/forms/setup.py33
-rw-r--r--docs/quick_tutorial/forms/tutorial/tests.py30
-rw-r--r--docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt11
-rw-r--r--docs/quick_tutorial/functional_testing.rst80
-rw-r--r--docs/quick_tutorial/functional_testing/development.ini36
-rw-r--r--docs/quick_tutorial/functional_testing/setup.py31
-rw-r--r--docs/quick_tutorial/hello_world.rst93
-rw-r--r--docs/quick_tutorial/hello_world/app.py13
-rw-r--r--docs/quick_tutorial/index.rst12
-rw-r--r--docs/quick_tutorial/ini.rst139
-rw-r--r--docs/quick_tutorial/ini/development.ini36
-rw-r--r--docs/quick_tutorial/ini/setup.py19
-rw-r--r--docs/quick_tutorial/jinja2.rst104
-rw-r--r--docs/quick_tutorial/jinja2/development.ini36
-rw-r--r--docs/quick_tutorial/jinja2/setup.py33
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/home.jinja24
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/tests.py8
-rw-r--r--docs/quick_tutorial/json.rst97
-rw-r--r--docs/quick_tutorial/json/development.ini36
-rw-r--r--docs/quick_tutorial/json/setup.py33
-rw-r--r--docs/quick_tutorial/json/tutorial/__init__.py4
-rw-r--r--docs/quick_tutorial/json/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/logging.rst65
-rw-r--r--docs/quick_tutorial/logging/development.ini5
-rw-r--r--docs/quick_tutorial/logging/setup.py33
-rw-r--r--docs/quick_tutorial/logging/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes.rst164
-rw-r--r--docs/quick_tutorial/more_view_classes/development.ini36
-rw-r--r--docs/quick_tutorial/more_view_classes/setup.py33
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/delete.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/edit.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/hello.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/views.py6
-rw-r--r--docs/quick_tutorial/package.rst84
-rw-r--r--docs/quick_tutorial/package/setup.py10
-rw-r--r--docs/quick_tutorial/package/tutorial/app.py15
-rw-r--r--docs/quick_tutorial/request_response.rst82
-rw-r--r--docs/quick_tutorial/request_response/development.ini36
-rw-r--r--docs/quick_tutorial/request_response/setup.py32
-rw-r--r--docs/quick_tutorial/requirements.rst259
-rw-r--r--docs/quick_tutorial/retail_forms/development.ini36
-rw-r--r--docs/quick_tutorial/retail_forms/setup.py21
-rw-r--r--docs/quick_tutorial/routing.rst87
-rw-r--r--docs/quick_tutorial/routing/development.ini36
-rw-r--r--docs/quick_tutorial/routing/setup.py33
-rw-r--r--docs/quick_tutorial/routing/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/scaffolds.rst86
-rw-r--r--docs/quick_tutorial/scaffolds/CHANGES.txt4
-rw-r--r--docs/quick_tutorial/scaffolds/MANIFEST.in2
-rw-r--r--docs/quick_tutorial/scaffolds/README.txt1
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/__init__.py12
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/favicon.icobin1406 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.pngbin333 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.pngbin203 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css8
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.pngbin2797 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css372
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.pngbin7044 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.pngbin33055 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gifbin49 -> 0 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt73
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/tests.py17
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/views.py6
-rw-r--r--docs/quick_tutorial/scaffolds/setup.py42
-rw-r--r--docs/quick_tutorial/sessions.rst86
-rw-r--r--docs/quick_tutorial/sessions/development.ini36
-rw-r--r--docs/quick_tutorial/sessions/setup.py33
-rw-r--r--docs/quick_tutorial/sessions/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/static_assets.rst70
-rw-r--r--docs/quick_tutorial/static_assets/development.ini36
-rw-r--r--docs/quick_tutorial/static_assets/setup.py33
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/tests.py4
-rw-r--r--docs/quick_tutorial/templating.rst128
-rw-r--r--docs/quick_tutorial/templating/development.ini36
-rw-r--r--docs/quick_tutorial/templating/setup.py29
-rw-r--r--docs/quick_tutorial/templating/tutorial/home.pt4
-rw-r--r--docs/quick_tutorial/tutorial_approach.rst69
-rw-r--r--docs/quick_tutorial/unit_testing.rst147
-rw-r--r--docs/quick_tutorial/unit_testing/development.ini36
-rw-r--r--docs/quick_tutorial/unit_testing/setup.py30
-rw-r--r--docs/quick_tutorial/view_classes.rst100
-rw-r--r--docs/quick_tutorial/view_classes/development.ini36
-rw-r--r--docs/quick_tutorial/view_classes/setup.py33
-rw-r--r--docs/quick_tutorial/views.rst97
-rw-r--r--docs/quick_tutorial/views/development.ini36
-rw-r--r--docs/quick_tutorial/views/setup.py31
136 files changed, 2594 insertions, 3131 deletions
diff --git a/docs/quick_tutorial/authentication.rst b/docs/quick_tutorial/authentication.rst
index 4b4eb1ba3..cd038ea36 100644
--- a/docs/quick_tutorial/authentication.rst
+++ b/docs/quick_tutorial/authentication.rst
@@ -1,29 +1,30 @@
+.. _qtut_authentication:
+
==============================
-20: Logins With Authentication
+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 of resources with authorization
+security statements.
-In the next step we will introduce protection 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
=====
@@ -32,26 +33,36 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes authentication; cd authentication
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r view_classes authentication; cd authentication
+
+#. Add ``bcrypt`` as a dependency in ``authentication/setup.py``:
+
+ .. literalinclude:: authentication/setup.py
+ :language: python
+ :emphasize-lines: 6
+ :linenos:
+
+#. We can now install our project in development mode:
+
+ .. code-block:: bash
+
+ $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:
@@ -67,7 +78,7 @@ Steps
: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
@@ -77,13 +88,13 @@ Steps
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ in a browser.
#. Click the "Log In" link.
-#. Submit the login form with the username ``editor`` and the password
+#. Submit the login form with the username ``editor`` and the password
``editor``.
#. Note that the "Log In" link has changed to "Logout".
@@ -93,42 +104,52 @@ Steps
Analysis
========
-Unlike many web frameworks, Pyramid includes a built-in (but optional)
-security model for authentication and authorization. This security
-system is intended to be flexible and support many needs. In this
-security model, authentication (who are you) and authorization (what
-are you allowed to do) are not just pluggable, but de-coupled. To learn
-one step at a time, we provide a system that identifies users and lets
-them log out.
-
-In this example we chose to use the bundled
-:ref:`AuthTktAuthenticationPolicy <authentication_module>`
-policy. We enabled it in our configuration and provided a
-ticket-signing secret in our INI file.
-
-Our view class grew a login view. When you reached it via a GET,
-it returned a login form. When reached via POST, it processed the
-username and password against the "groupfinder" callable that we
-registered in the configuration.
-
-In our template, we fetched the ``logged_in`` value from the view
-class. We use this to calculate the logged-in user,
-if any. In the template we can then choose to show a login link to
-anonymous visitors or a logout link to logged-in users.
-
-Extra Credit
+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 decoupled. To learn one step at a time, we provide a system that identifies
+users and lets them log out.
+
+In this example we chose to use the bundled :ref:`AuthTktAuthenticationPolicy
+<authentication_module>` policy. We enabled it in our configuration and
+provided a ticket-signing secret in our INI file.
+
+Our view class grew a login view. When you reached it via a ``GET`` 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.
+
+The function ``hash_password`` uses a one-way hashing algorithm with a salt on
+the user's password via ``bcrypt``, instead of storing the password in plain
+text. This is considered to be a "best practice" for security.
+
+.. note::
+ There are alternative libraries to ``bcrypt`` if it is an issue on your
+ system. Just make sure that the library uses an algorithm approved for
+ storing passwords securely.
+
+The function ``check_password`` will compare the two hashed values of the
+submitted password and the user's password stored in the database. If the
+hashed values are equivalent, then the user is authenticated, else
+authentication fails.
+
+In our template, we fetched the ``logged_in`` value from the view class. We use
+this to calculate the logged-in user, if any. In the template we can then
+choose to show a login link to anonymous visitors or a logout link to logged-in
+users.
+
+
+Extra credit
============
#. What is the difference between a user and a principal?
#. Can I use a database behind my ``groupfinder`` to look up principals?
-#. Do I have to put a ``renderer`` in my ``@forbidden_view_config``
- decorator?
-
-#. Once I am logged in, does any user-centric information get jammed
- onto each request? Use ``import pdb; pdb.set_trace()`` to answer
- this.
+#. 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>`.
+ :ref:`AuthTktAuthenticationPolicy <authentication_module>`, `bcrypt
+ <https://pypi.org/project/bcrypt/>`_
diff --git a/docs/quick_tutorial/authentication/development.ini b/docs/quick_tutorial/authentication/development.ini
index 5d4580ff5..cae509542 100644
--- a/docs/quick_tutorial/authentication/development.ini
+++ b/docs/quick_tutorial/authentication/development.ini
@@ -6,37 +6,5 @@ pyramid.includes =
tutorial.secret = 98zd
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/authentication/setup.py b/docs/quick_tutorial/authentication/setup.py
index 2221b72e9..64366a2df 100644
--- a/docs/quick_tutorial/authentication/setup.py
+++ b/docs/quick_tutorial/authentication/setup.py
@@ -1,14 +1,32 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
+ 'bcrypt',
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/authentication/tutorial/home.pt b/docs/quick_tutorial/authentication/tutorial/home.pt
index 6ecd0081b..ed911b673 100644
--- a/docs/quick_tutorial/authentication/tutorial/home.pt
+++ b/docs/quick_tutorial/authentication/tutorial/home.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
@@ -15,4 +15,4 @@
<h1>Hi ${name}</h1>
<p>Visit <a href="${request.route_url('hello')}">hello</a></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authentication/tutorial/login.pt b/docs/quick_tutorial/authentication/tutorial/login.pt
index 4451fc4f8..9e5bfe2ad 100644
--- a/docs/quick_tutorial/authentication/tutorial/login.pt
+++ b/docs/quick_tutorial/authentication/tutorial/login.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Login</h1>
@@ -22,4 +22,4 @@
value="Log In"/>
</form>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authentication/tutorial/security.py b/docs/quick_tutorial/authentication/tutorial/security.py
index ab90bab2c..e585e2642 100644
--- a/docs/quick_tutorial/authentication/tutorial/security.py
+++ b/docs/quick_tutorial/authentication/tutorial/security.py
@@ -1,5 +1,17 @@
-USERS = {'editor': 'editor',
- 'viewer': 'viewer'}
+import bcrypt
+
+
+def hash_password(pw):
+ pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
+ return pwhash.decode('utf8')
+
+def check_password(pw, hashed_pw):
+ expected_hash = hashed_pw.encode('utf8')
+ return bcrypt.checkpw(pw.encode('utf8'), expected_hash)
+
+
+USERS = {'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer')}
GROUPS = {'editor': ['group:editors']}
diff --git a/docs/quick_tutorial/authentication/tutorial/views.py b/docs/quick_tutorial/authentication/tutorial/views.py
index ab46eb2dd..b2d9354ec 100644
--- a/docs/quick_tutorial/authentication/tutorial/views.py
+++ b/docs/quick_tutorial/authentication/tutorial/views.py
@@ -9,7 +9,10 @@ from pyramid.view import (
view_defaults
)
-from .security import USERS
+from .security import (
+ USERS,
+ check_password
+)
@view_defaults(renderer='home.pt')
@@ -40,7 +43,8 @@ class TutorialViews:
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
- if USERS.get(login) == password:
+ hashed_pw = USERS.get(login)
+ if hashed_pw and check_password(password, hashed_pw):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
diff --git a/docs/quick_tutorial/authorization.rst b/docs/quick_tutorial/authorization.rst
index 6b10d3409..e80f88c51 100644
--- a/docs/quick_tutorial/authorization.rst
+++ b/docs/quick_tutorial/authorization.rst
@@ -1,34 +1,38 @@
+.. _qtut_authorization:
+
===========================================
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
=====
@@ -37,76 +41,77 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r authentication authorization; cd authorization
- $ $VENV/bin/python setup.py develop
+ 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:
+ :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:
+ :linenos:
#. Change ``authorization/tutorial/views.py`` to require the ``edit``
permission on the ``hello`` view and implement the forbidden view:
.. literalinclude:: authorization/tutorial/views.py
- :linenos:
+ :linenos:
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ in a browser.
#. If you are still logged in, click the "Log Out" link.
-#. Visit http://localhost:6543/howdy in a browser. You should be
- asked to login.
+#. 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 ``/hello``, you need to get
-shown the login screen. How does Pyramid know what is the login page to
-use? We explicitly told Pyramid that the ``login`` view should be used
-by decorating the view with ``@forbidden_view_config``.
-Extra Credit
+Extra credit
============
-#. Perhaps you would like experience of not having enough permissions
+#. Do I have to put a ``renderer`` in my ``@forbidden_view_config`` decorator?
+
+#. Perhaps you would like the experience of not having enough permissions
(forbidden) to be richer. How could you change this?
-#. Perhaps we want to store security statements in a database and
- 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?
diff --git a/docs/quick_tutorial/authorization/development.ini b/docs/quick_tutorial/authorization/development.ini
index 5d4580ff5..cae509542 100644
--- a/docs/quick_tutorial/authorization/development.ini
+++ b/docs/quick_tutorial/authorization/development.ini
@@ -6,37 +6,5 @@ pyramid.includes =
tutorial.secret = 98zd
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/authorization/setup.py b/docs/quick_tutorial/authorization/setup.py
index 2221b72e9..64366a2df 100644
--- a/docs/quick_tutorial/authorization/setup.py
+++ b/docs/quick_tutorial/authorization/setup.py
@@ -1,14 +1,32 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
+ 'bcrypt',
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/authorization/tutorial/home.pt b/docs/quick_tutorial/authorization/tutorial/home.pt
index 6ecd0081b..ed911b673 100644
--- a/docs/quick_tutorial/authorization/tutorial/home.pt
+++ b/docs/quick_tutorial/authorization/tutorial/home.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
@@ -15,4 +15,4 @@
<h1>Hi ${name}</h1>
<p>Visit <a href="${request.route_url('hello')}">hello</a></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authorization/tutorial/login.pt b/docs/quick_tutorial/authorization/tutorial/login.pt
index 4451fc4f8..9e5bfe2ad 100644
--- a/docs/quick_tutorial/authorization/tutorial/login.pt
+++ b/docs/quick_tutorial/authorization/tutorial/login.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Login</h1>
@@ -22,4 +22,4 @@
value="Log In"/>
</form>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/authorization/tutorial/security.py b/docs/quick_tutorial/authorization/tutorial/security.py
index ab90bab2c..e585e2642 100644
--- a/docs/quick_tutorial/authorization/tutorial/security.py
+++ b/docs/quick_tutorial/authorization/tutorial/security.py
@@ -1,5 +1,17 @@
-USERS = {'editor': 'editor',
- 'viewer': 'viewer'}
+import bcrypt
+
+
+def hash_password(pw):
+ pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
+ return pwhash.decode('utf8')
+
+def check_password(pw, hashed_pw):
+ expected_hash = hashed_pw.encode('utf8')
+ return bcrypt.checkpw(pw.encode('utf8'), expected_hash)
+
+
+USERS = {'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer')}
GROUPS = {'editor': ['group:editors']}
diff --git a/docs/quick_tutorial/authorization/tutorial/views.py b/docs/quick_tutorial/authorization/tutorial/views.py
index 43d14455a..3876efb1c 100644
--- a/docs/quick_tutorial/authorization/tutorial/views.py
+++ b/docs/quick_tutorial/authorization/tutorial/views.py
@@ -10,7 +10,10 @@ from pyramid.view import (
forbidden_view_config
)
-from .security import USERS
+from .security import (
+ USERS,
+ check_password
+)
@view_defaults(renderer='home.pt')
@@ -42,7 +45,8 @@ class TutorialViews:
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
- if USERS.get(login) == password:
+ hashed_pw = USERS.get(login)
+ if hashed_pw and check_password(password, hashed_pw):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
diff --git a/docs/quick_tutorial/conf.py b/docs/quick_tutorial/conf.py
deleted file mode 100644
index 47b8fae41..000000000
--- a/docs/quick_tutorial/conf.py
+++ /dev/null
@@ -1,281 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Getting Started with Pyramid and REST documentation build configuration file, created by
-# sphinx-quickstart on Mon Aug 26 14:44:57 2013.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.intersphinx']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'Getting Started with Pyramid and REST'
-copyright = u'2013, Agendaless Consulting'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '1.0'
-# The full version, including alpha/beta/rc tags.
-release = '1.0'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'GettingStartedwithPyramidandRESTdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
- # The paper size ('letterpaper' or 'a4paper').
- #'papersize': 'letterpaper',
-
- # The font size ('10pt', '11pt' or '12pt').
- #'pointsize': '10pt',
-
- # Additional stuff for the LaTeX preamble.
- #'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'GettingStartedwithPyramidandREST.tex',
- u'Getting Started with Pyramid and REST Documentation',
- u'Agendaless Consulting', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'gettingstartedwithpyramidandrest',
- u'Getting Started with Pyramid and REST Documentation',
- [u'Agendaless Consulting'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'GettingStartedwithPyramidandREST',
- u'Getting Started with Pyramid and REST Documentation',
- u'Agendaless Consulting', 'GettingStartedwithPyramidandREST',
- 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
-
-
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {
- 'python': (
- 'http://docs.python.org/2',
- None),
- 'sqla': (
- 'http://docs.sqlalchemy.org/en/latest',
- None),
- 'pyramid': (
- 'http://docs.pylonsproject.org/projects/pyramid/en/latest/',
- None),
- 'jinja2': (
- 'http://docs.pylonsproject.org/projects/pyramid_jinja2/en/latest/',
- None),
- 'toolbar': (
- 'http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest',
- None),
- 'deform': (
- 'http://docs.pylonsproject.org/projects/deform/en/latest',
- None),
- 'colander': (
- 'http://docs.pylonsproject.org/projects/colander/en/latest',
- None),
- 'tutorials': (
- 'http://docs.pylonsproject.org/projects/pyramid_tutorials/en/latest/',
- None),
-}
diff --git a/docs/quick_tutorial/cookiecutters.rst b/docs/quick_tutorial/cookiecutters.rst
new file mode 100644
index 000000000..e4a585a33
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters.rst
@@ -0,0 +1,92 @@
+.. _qtut_cookiecutters:
+
+=================================================
+Prelude: Quick Project Startup with Cookiecutters
+=================================================
+
+To ease the process of getting started on a project, the Pylons Project provides a :term:`cookiecutter` that generates sample :app:`Pyramid` projects from project templates. The cookiecutter will install :app:`Pyramid` and its dependencies as well. We will still cover many topics of web application development using :app:`Pyramid`, but it's good to know of this facility. This prelude will demonstrate how to get a working :app:`Pyramid` web application running via ``cookiecutter``.
+
+
+Objectives
+==========
+
+- Use a cookiecutter to make a new project.
+
+- Start up a :app:`Pyramid` application and visit it in a web browser.
+
+
+Steps
+=====
+
+#. Install cookiecutter into your virtual environment.
+
+ .. code-block:: bash
+
+ $VENV/bin/pip install cookiecutter
+
+#. Let's use the cookiecutter ``pyramid-cookiecutter-starter`` to create a starter :app:`Pyramid` project in the current directory, entering values at the prompts as shown below for the following command.
+
+ .. code-block:: bash
+
+ $VENV/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout master
+
+ If prompted for the first item, accept the default ``yes`` by hitting return.
+
+ .. code-block:: text
+
+ You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
+ Is it okay to delete and re-clone it? [yes]: yes
+ project_name [Pyramid Scaffold]: cc_starter
+ repo_name [cc_starter]: cc_starter
+ Select template_language:
+ 1 - jinja2
+ 2 - chameleon
+ 3 - mako
+ Choose from 1, 2, 3 [1]: 1
+ Select backend:
+ 1 - none
+ 2 - sqlalchemy
+ 3 - zodb
+ Choose from 1, 2, 3 [1]: 1
+
+#. We then run through the following commands.
+
+ .. code-block:: bash
+
+ # Change directory into your newly created project.
+ cd cc_starter
+ # Create a new virtual environment...
+ python3 -m venv env
+ # ...where we upgrade packaging tools...
+ env/bin/pip install --upgrade pip setuptools
+ # ...and into which we install our project.
+ env/bin/pip install -e .
+
+#. Start up the application by pointing :app:`Pyramid`'s ``pserve`` command at the
+ project's (generated) configuration file:
+
+ .. code-block:: bash
+
+ env/bin/pserve development.ini --reload
+
+ On start up, ``pserve`` logs some output:
+
+ .. code-block:: text
+
+ Starting subprocess with file monitor
+ Starting server in PID 73732.
+ Serving on http://localhost:6543
+ Serving on http://localhost:6543
+
+#. Open http://localhost:6543/ in your browser.
+
+Analysis
+========
+
+Rather than starting from scratch, a cookiecutter can make it easy to get a Python
+project containing a working :app:`Pyramid` application.
+
+``pserve`` is :app:`Pyramid`'s application runner, separating operational details from
+your code. When you install :app:`Pyramid`, a small command program called ``pserve``
+is written to your ``bin`` directory. This program is an executable Python
+module. It is passed a configuration file (in this case, ``development.ini``).
diff --git a/docs/quick_tutorial/cookiecutters/.coveragerc b/docs/quick_tutorial/cookiecutters/.coveragerc
new file mode 100644
index 000000000..1bcbb8c3e
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/.coveragerc
@@ -0,0 +1,3 @@
+[run]
+source = cc_starter
+omit = cc_starter/test*
diff --git a/docs/quick_tutorial/cookiecutters/.gitignore b/docs/quick_tutorial/cookiecutters/.gitignore
new file mode 100644
index 000000000..1853d983c
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/.gitignore
@@ -0,0 +1,21 @@
+*.egg
+*.egg-info
+*.pyc
+*$py.class
+*~
+.coverage
+coverage.xml
+build/
+dist/
+.tox/
+nosetests.xml
+env*/
+tmp/
+Data.fs*
+*.sublime-project
+*.sublime-workspace
+.*.sw?
+.sw?
+.DS_Store
+coverage
+test
diff --git a/docs/quick_tutorial/cookiecutters/CHANGES.txt b/docs/quick_tutorial/cookiecutters/CHANGES.txt
new file mode 100644
index 000000000..14b902fd1
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version.
diff --git a/docs/quick_tutorial/cookiecutters/MANIFEST.in b/docs/quick_tutorial/cookiecutters/MANIFEST.in
new file mode 100644
index 000000000..79c7ec16c
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/MANIFEST.in
@@ -0,0 +1,2 @@
+include *.txt *.ini *.cfg *.rst
+recursive-include cc_starter *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2
diff --git a/docs/quick_tutorial/cookiecutters/README.txt b/docs/quick_tutorial/cookiecutters/README.txt
new file mode 100644
index 000000000..55c5dcec6
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/README.txt
@@ -0,0 +1,29 @@
+cc_starter
+==========
+
+Getting Started
+---------------
+
+- Change directory into your newly created project.
+
+ cd cc_starter
+
+- Create a Python virtual environment.
+
+ python3 -m venv env
+
+- Upgrade packaging tools.
+
+ env/bin/pip install --upgrade pip setuptools
+
+- Install the project in editable mode with its testing requirements.
+
+ env/bin/pip install -e ".[testing]"
+
+- Run your project's tests.
+
+ env/bin/pytest
+
+- Run your project.
+
+ env/bin/pserve development.ini
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/__init__.py b/docs/quick_tutorial/cookiecutters/cc_starter/__init__.py
new file mode 100644
index 000000000..a3d5a6469
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/__init__.py
@@ -0,0 +1,11 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ """ This function returns a Pyramid WSGI application.
+ """
+ with Configurator(settings=settings) as config:
+ config.include('pyramid_jinja2')
+ config.include('.routes')
+ config.scan()
+ return config.make_wsgi_app()
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/routes.py b/docs/quick_tutorial/cookiecutters/cc_starter/routes.py
new file mode 100644
index 000000000..25504ad4d
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/routes.py
@@ -0,0 +1,3 @@
+def includeme(config):
+ config.add_static_view('static', 'static', cache_max_age=3600)
+ config.add_route('home', '/')
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid-16x16.png b/docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid-16x16.png
new file mode 100644
index 000000000..979203112
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid-16x16.png
Binary files differ
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid.png b/docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid.png
new file mode 100644
index 000000000..4ab837be9
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/static/pyramid.png
Binary files differ
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/static/theme.css b/docs/quick_tutorial/cookiecutters/cc_starter/static/theme.css
new file mode 100644
index 000000000..0f4b1a4d4
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/static/theme.css
@@ -0,0 +1,154 @@
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
+body {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: #ffffff;
+ background: #bc2131;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+}
+p {
+ font-weight: 300;
+}
+.font-normal {
+ font-weight: 400;
+}
+.font-semi-bold {
+ font-weight: 600;
+}
+.font-bold {
+ font-weight: 700;
+}
+.starter-template {
+ margin-top: 250px;
+}
+.starter-template .content {
+ margin-left: 10px;
+}
+.starter-template .content h1 {
+ margin-top: 10px;
+ font-size: 60px;
+}
+.starter-template .content h1 .smaller {
+ font-size: 40px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead {
+ font-size: 25px;
+ color: #f2b7bd;
+}
+.starter-template .content .lead .font-normal {
+ color: #ffffff;
+}
+.starter-template .links {
+ float: right;
+ right: 0;
+ margin-top: 125px;
+}
+.starter-template .links ul {
+ display: block;
+ padding: 0;
+ margin: 0;
+}
+.starter-template .links ul li {
+ list-style: none;
+ display: inline;
+ margin: 0 10px;
+}
+.starter-template .links ul li:first-child {
+ margin-left: 0;
+}
+.starter-template .links ul li:last-child {
+ margin-right: 0;
+}
+.starter-template .links ul li.current-version {
+ color: #f2b7bd;
+ font-weight: 400;
+}
+.starter-template .links ul li a, a {
+ color: #f2b7bd;
+ text-decoration: underline;
+}
+.starter-template .links ul li a:hover, a:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+.starter-template .links ul li .icon-muted {
+ color: #eb8b95;
+ margin-right: 5px;
+}
+.starter-template .links ul li:hover .icon-muted {
+ color: #ffffff;
+}
+.starter-template .copyright {
+ margin-top: 10px;
+ font-size: 0.9em;
+ color: #f2b7bd;
+ text-transform: lowercase;
+ float: right;
+ right: 0;
+}
+@media (max-width: 1199px) {
+ .starter-template .content h1 {
+ font-size: 45px;
+ }
+ .starter-template .content h1 .smaller {
+ font-size: 30px;
+ }
+ .starter-template .content .lead {
+ font-size: 20px;
+ }
+}
+@media (max-width: 991px) {
+ .starter-template {
+ margin-top: 0;
+ }
+ .starter-template .logo {
+ margin: 40px auto;
+ }
+ .starter-template .content {
+ margin-left: 0;
+ text-align: center;
+ }
+ .starter-template .content h1 {
+ margin-bottom: 20px;
+ }
+ .starter-template .links {
+ float: none;
+ text-align: center;
+ margin-top: 60px;
+ }
+ .starter-template .copyright {
+ float: none;
+ text-align: center;
+ }
+}
+@media (max-width: 767px) {
+ .starter-template .content h1 .smaller {
+ font-size: 25px;
+ display: block;
+ }
+ .starter-template .content .lead {
+ font-size: 16px;
+ }
+ .starter-template .links {
+ margin-top: 40px;
+ }
+ .starter-template .links ul li {
+ display: block;
+ margin: 0;
+ }
+ .starter-template .links ul li .icon-muted {
+ display: none;
+ }
+ .starter-template .copyright {
+ margin-top: 20px;
+ }
+}
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/templates/404.jinja2 b/docs/quick_tutorial/cookiecutters/cc_starter/templates/404.jinja2
new file mode 100644
index 000000000..aaf12413f
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/templates/404.jinja2
@@ -0,0 +1,8 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1>
+ <p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p>
+</div>
+{% endblock content %}
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2 b/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2
new file mode 100644
index 000000000..90d2d106a
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html lang="{{request.locale_name}}">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="pyramid web application">
+ <meta name="author" content="Pylons Project">
+ <link rel="shortcut icon" href="{{request.static_url('cc_starter:static/pyramid-16x16.png')}}">
+
+ <title>Cookiecutter Starter project for the Pyramid Web Framework</title>
+
+ <!-- Bootstrap core CSS -->
+ <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
+
+ <!-- Custom styles for this scaffold -->
+ <link href="{{request.static_url('cc_starter:static/theme.css')}}" rel="stylesheet">
+
+ <!-- HTML5 shiv and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js" integrity="sha384-0s5Pv64cNZJieYFkXYOTId2HMA2Lfb6q2nAcx2n0RTLUnCAoTTsS0nKEO27XyKcY" crossorigin="anonymous"></script>
+ <script src="//oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js" integrity="sha384-ZoaMbDF+4LeFxg6WdScQ9nnR1QC2MIRxA1O9KWEXQwns1G8UNyIEZIQidzb0T1fo" crossorigin="anonymous"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+
+ <div class="starter-template">
+ <div class="container">
+ <div class="row">
+ <div class="col-md-2">
+ <img class="logo img-responsive" src="{{request.static_url('cc_starter:static/pyramid.png') }}" alt="pyramid web framework">
+ </div>
+ <div class="col-md-10">
+ {% block content %}
+ <p>No content</p>
+ {% endblock content %}
+ </div>
+ </div>
+ <div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
+ </ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="copyright">
+ Copyright &copy; Pylons Project
+ </div>
+ </div>
+ </div>
+ </div>
+
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="//code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
+ <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
+ </body>
+</html>
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/templates/mytemplate.jinja2 b/docs/quick_tutorial/cookiecutters/cc_starter/templates/mytemplate.jinja2
new file mode 100644
index 000000000..f2e7283f8
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/templates/mytemplate.jinja2
@@ -0,0 +1,8 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1>
+ <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a&nbsp;Pyramid application generated&nbsp;by<br><span class="font-normal">Cookiecutter</span>.</p>
+</div>
+{% endblock content %}
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/tests.py b/docs/quick_tutorial/cookiecutters/cc_starter/tests.py
new file mode 100644
index 000000000..f3886be84
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/tests.py
@@ -0,0 +1,29 @@
+import unittest
+
+from pyramid import testing
+
+
+class ViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_my_view(self):
+ from .views.default import my_view
+ request = testing.DummyRequest()
+ info = my_view(request)
+ self.assertEqual(info['project'], 'cc_starter')
+
+
+class FunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from cc_starter import main
+ app = main({})
+ from webtest import TestApp
+ self.testapp = TestApp(app)
+
+ def test_root(self):
+ res = self.testapp.get('/', status=200)
+ self.assertTrue(b'Pyramid' in res.body)
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/views/__init__.py b/docs/quick_tutorial/cookiecutters/cc_starter/views/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/views/__init__.py
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/views/default.py b/docs/quick_tutorial/cookiecutters/cc_starter/views/default.py
new file mode 100644
index 000000000..47af359b5
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/views/default.py
@@ -0,0 +1,6 @@
+from pyramid.view import view_config
+
+
+@view_config(route_name='home', renderer='../templates/mytemplate.jinja2')
+def my_view(request):
+ return {'project': 'cc_starter'}
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/views/notfound.py b/docs/quick_tutorial/cookiecutters/cc_starter/views/notfound.py
new file mode 100644
index 000000000..69d6e2804
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/views/notfound.py
@@ -0,0 +1,7 @@
+from pyramid.view import notfound_view_config
+
+
+@notfound_view_config(renderer='../templates/404.jinja2')
+def notfound_view(request):
+ request.response.status = 404
+ return {}
diff --git a/docs/quick_tutorial/scaffolds/development.ini b/docs/quick_tutorial/cookiecutters/development.ini
index b31d06194..ec621169d 100644
--- a/docs/quick_tutorial/scaffolds/development.ini
+++ b/docs/quick_tutorial/cookiecutters/development.ini
@@ -1,17 +1,17 @@
###
# app configuration
-# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
-use = egg:scaffolds
+use = egg:cc_starter
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
-pyramid.includes =
+pyramid.includes =
pyramid_debugtoolbar
# By default, the toolbar only appears for clients from IP addresses
@@ -24,16 +24,15 @@ pyramid.includes =
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
-port = 6543
+listen = localhost:6543
###
# logging configuration
-# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
-keys = root, scaffolds
+keys = root, cc_starter
[handlers]
keys = console
@@ -45,10 +44,10 @@ keys = generic
level = INFO
handlers = console
-[logger_scaffolds]
+[logger_cc_starter]
level = DEBUG
handlers =
-qualname = scaffolds
+qualname = cc_starter
[handler_console]
class = StreamHandler
@@ -57,4 +56,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/docs/quick_tutorial/scaffolds/production.ini b/docs/quick_tutorial/cookiecutters/production.ini
index 1418e6bf6..8d2b9c79d 100644
--- a/docs/quick_tutorial/scaffolds/production.ini
+++ b/docs/quick_tutorial/cookiecutters/production.ini
@@ -1,10 +1,10 @@
###
# app configuration
-# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
-use = egg:scaffolds
+use = egg:cc_starter
pyramid.reload_templates = false
pyramid.debug_authorization = false
@@ -18,16 +18,15 @@ pyramid.default_locale_name = en
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
-port = 6543
+listen = *:6543
###
# logging configuration
-# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
-keys = root, scaffolds
+keys = root, cc_starter
[handlers]
keys = console
@@ -39,10 +38,10 @@ keys = generic
level = WARN
handlers = console
-[logger_scaffolds]
+[logger_cc_starter]
level = WARN
handlers =
-qualname = scaffolds
+qualname = cc_starter
[handler_console]
class = StreamHandler
@@ -51,4 +50,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/docs/quick_tutorial/cookiecutters/pytest.ini b/docs/quick_tutorial/cookiecutters/pytest.ini
new file mode 100644
index 000000000..a7bd797f0
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+testpaths = cc_starter
+python_files = *.py
diff --git a/docs/quick_tutorial/cookiecutters/setup.py b/docs/quick_tutorial/cookiecutters/setup.py
new file mode 100644
index 000000000..9482e7c32
--- /dev/null
+++ b/docs/quick_tutorial/cookiecutters/setup.py
@@ -0,0 +1,52 @@
+import os
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+with open(os.path.join(here, 'README.txt')) as f:
+ README = f.read()
+with open(os.path.join(here, 'CHANGES.txt')) as f:
+ CHANGES = f.read()
+
+requires = [
+ 'plaster_pastedeploy',
+ 'pyramid',
+ 'pyramid_jinja2',
+ 'pyramid_debugtoolbar',
+ 'waitress',
+]
+
+tests_require = [
+ 'WebTest >= 1.3.1', # py3 compat
+ 'pytest>=3.7.4',
+ 'pytest-cov',
+]
+
+setup(
+ name='cc_starter',
+ version='0.0',
+ description='cc_starter',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ 'Programming Language :: Python',
+ 'Framework :: Pyramid',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
+ ],
+ author='',
+ author_email='',
+ url='',
+ keywords='web pyramid pylons',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ extras_require={
+ 'testing': tests_require,
+ },
+ install_requires=requires,
+ entry_points={
+ 'paste.app_factory': [
+ 'main = cc_starter:main',
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/databases.rst b/docs/quick_tutorial/databases.rst
index 20b3cd46d..db75d70ce 100644
--- a/docs/quick_tutorial/databases.rst
+++ b/docs/quick_tutorial/databases.rst
@@ -4,183 +4,187 @@
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 Pyramid cookiecutter ``pyramid-cookiecutter-starter`` is really
+ helpful for getting a SQLAlchemy project going, including generation of
+ the console script. Since we want to see all the decisions, we will forgo
+ convenience in this tutorial, and wire it up ourselves.
+
Objectives
==========
-- Store pages in SQLite by using SQLAlchemy models
+- 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
=====
-#. We are going to use the forms step as our starting point:
+#. We are going to use the forms step as our starting point:
+
+ .. code-block:: bash
+
+ cd ..; cp -r forms databases; cd databases
- .. code-block:: bash
+#. We need to add some dependencies in ``databases/setup.py`` as well as an :term:`entry point` for the command-line script:
- $ cd ..; cp -r forms databases; cd databases
+ .. literalinclude:: databases/setup.py
+ :linenos:
+ :emphasize-lines: 9-10, 12, 34-36
-#. We need to add some dependencies in ``databases/setup.py`` as well
- as an "entry point" for the command-line script:
+ .. note:: We aren't yet doing ``$VENV/bin/pip install -e .`` because we need to write a script and update configuration first.
- .. literalinclude:: databases/setup.py
- :linenos:
+#. Our configuration file at ``databases/development.ini`` wires together some new pieces:
- .. note::
+ .. literalinclude:: databases/development.ini
+ :language: ini
- We aren't yet doing ``python3.3 setup.py develop`` as we
- are changing it later.
+#. This engine configuration now needs to be read into the application through changes in ``databases/tutorial/__init__.py``:
-#. Our configuration file at ``databases/development.ini`` wires
- together some new pieces:
+ .. literalinclude:: databases/tutorial/__init__.py
+ :linenos:
- .. literalinclude:: databases/development.ini
- :language: ini
+#. Make a command-line script at ``databases/tutorial/initialize_db.py`` to initialize the database:
-#. This engine configuration now needs to be read into the application
- through changes in ``databases/tutorial/__init__.py``:
+ .. literalinclude:: databases/tutorial/initialize_db.py
+ :linenos:
- .. literalinclude:: databases/tutorial/__init__.py
- :linenos:
+#. Now that we've got all the pieces ready, and because we changed ``setup.py``, we now install all the goodies:
-#. Make a command-line script at ``databases/tutorial/initialize_db.py``
- to initialize the database:
+ .. code-block:: bash
- .. literalinclude:: databases/tutorial/initialize_db.py
+ $VENV/bin/pip install -e .
-#. Since ``setup.py`` changed, we now run it:
+#. The script references some models in ``databases/tutorial/models.py``:
- .. code-block:: bash
+ .. literalinclude:: databases/tutorial/models.py
+ :linenos:
- $ $VENV/bin/python setup.py develop
+#. Let's run this console script, thus producing our database and table:
-#. The script references some models in ``databases/tutorial/models.py``:
+ .. code-block:: bash
- .. literalinclude:: databases/tutorial/models.py
- :linenos:
+ $VENV/bin/initialize_tutorial_db development.ini
-#. Let's run this console script, thus producing our database and table:
+ 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)
+ )
- .. code-block:: bash
- $ $VENV/bin/initialize_tutorial_db development.ini
- 2013-09-06 15:54:08,050 INFO [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages")
- 2013-09-06 15:54:08,050 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
- 2013-09-06 15:54:08,051 INFO [sqlalchemy.engine.base.Engine][MainThread]
- CREATE TABLE wikipages (
- uid INTEGER NOT NULL,
- title TEXT,
- body TEXT,
- PRIMARY KEY (uid),
- UNIQUE (title)
- )
+ 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
+ .. 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:
+ .. literalinclude:: databases/tutorial/tests.py
+ :linenos:
-#. Run the tests in your package using ``nose``:
+#. Run the tests in your package using ``pytest``:
.. code-block:: bash
- $ $VENV/bin/nosetests .
+ $VENV/bin/pytest tutorial/tests.py -q
..
- -----------------------------------------------------------------
- Ran 2 tests in 1.141s
+ 2 passed in 1.41 seconds
- OK
+#. Run your Pyramid application with:
-#. Run your Pyramid application with:
+ .. code-block:: bash
- .. code-block:: bash
+ $VENV/bin/pserve development.ini --reload
- $ $VENV/bin/pserve development.ini --reload
+#. Open http://localhost:6543/ in a browser.
-#. 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 app
-application, either as middleware or a Pyramid "tween". Then,
-just before you return the response, all transaction-aware parts of
-your application are executed.
+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.
-
-The ``initialize_tutorial_db`` is a nice example of framework support.
-You point your setup at the location of some ``[console_scripts]`` and
-these get generated into your virtualenv's ``bin`` directory. Our
-console script follows the pattern of being fed a configuration file
-with all the bootstrapping. It then opens SQLAlchemy and creates the
-root of the wiki, which also makes the SQLite file. Note the
-``with transaction.manager`` part that puts the work in the scope of a
-transaction (as we aren't inside a web request where this is done
-automatically.)
-
-The ``models.py`` does a little bit extra work to hook up SQLAlchemy
-into the Pyramid transaction manager. It then declares the model for a
-``Page``.
+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 ``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.
diff --git a/docs/quick_tutorial/databases/development.ini b/docs/quick_tutorial/databases/development.ini
index 270da960f..270cf7b63 100644
--- a/docs/quick_tutorial/databases/development.ini
+++ b/docs/quick_tutorial/databases/development.ini
@@ -8,25 +8,19 @@ pyramid.includes =
sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
+use = egg:waitress#main
+listen = localhost:6543
# Begin logging configuration
[loggers]
-keys = root, tutorial, sqlalchemy
+keys = root, tutorial, sqlalchemy.engine.base.Engine
[logger_tutorial]
level = DEBUG
handlers =
qualname = tutorial
-[logger_sqlalchemy]
-level = INFO
-handlers =
-qualname = sqlalchemy.engine
-
[handlers]
keys = console
@@ -37,6 +31,11 @@ keys = generic
level = INFO
handlers = console
+[logger_sqlalchemy.engine.base.Engine]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine.base.Engine
+
[handler_console]
class = StreamHandler
args = (sys.stderr,)
diff --git a/docs/quick_tutorial/databases/setup.py b/docs/quick_tutorial/databases/setup.py
index 238358fe4..c4e4ae2f2 100644
--- a/docs/quick_tutorial/databases/setup.py
+++ b/docs/quick_tutorial/databases/setup.py
@@ -1,20 +1,38 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
+ 'deform',
'pyramid',
'pyramid_chameleon',
- 'deform',
- 'sqlalchemy',
'pyramid_tm',
- 'zope.sqlalchemy'
+ 'sqlalchemy',
+ 'waitress',
+ 'zope.sqlalchemy',
+]
+
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- [console_scripts]
- initialize_tutorial_db = tutorial.initialize_db:main
- """,
-) \ No newline at end of file
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ 'console_scripts': [
+ 'initialize_tutorial_db = tutorial.initialize_db:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/databases/sqltutorial.sqlite b/docs/quick_tutorial/databases/sqltutorial.sqlite
deleted file mode 100644
index b8bd856fd..000000000
--- a/docs/quick_tutorial/databases/sqltutorial.sqlite
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/databases/tutorial/tests.py b/docs/quick_tutorial/databases/tutorial/tests.py
index e18e70c8c..11e747d15 100644
--- a/docs/quick_tutorial/databases/tutorial/tests.py
+++ b/docs/quick_tutorial/databases/tutorial/tests.py
@@ -40,16 +40,14 @@ class WikiViewTests(unittest.TestCase):
class WikiFunctionalTests(unittest.TestCase):
def setUp(self):
- self.session = _initTestingDB()
- self.config = testing.setUp()
from pyramid.paster import get_app
app = get_app('development.ini')
from webtest import TestApp
self.testapp = TestApp(app)
def tearDown(self):
- self.session.remove()
- testing.tearDown()
+ from .models import DBSession
+ DBSession.remove()
def test_it(self):
res = self.testapp.get('/', status=200)
diff --git a/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
index d1fea0d7f..25bab04d0 100644
--- a/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
+++ b/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
@@ -2,12 +2,23 @@
<html lang="en">
<head>
<title>WikiPage: Add/Edit</title>
+ <link rel="stylesheet"
+ href="${request.static_url('deform:static/css/bootstrap.min.css')}"
+ type="text/css" media="screen" charset="utf-8"/>
+ <link rel="stylesheet"
+ href="${request.static_url('deform:static/css/form.css')}"
+ type="text/css"/>
<tal:block tal:repeat="reqt view.reqts['css']">
<link rel="stylesheet" type="text/css"
- href="${request.static_url('deform:static/' + reqt)}"/>
+ href="${request.static_url(reqt)}"/>
</tal:block>
+ <script src="${request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}"
+ type="text/javascript"></script>
+ <script src="${request.static_url('deform:static/scripts/bootstrap.min.js')}"
+ type="text/javascript"></script>
+
<tal:block tal:repeat="reqt view.reqts['js']">
- <script src="${request.static_url('deform:static/' + reqt)}"
+ <script src="${request.static_url(reqt)}"
type="text/javascript"></script>
</tal:block>
</head>
diff --git a/docs/quick_tutorial/debugtoolbar.rst b/docs/quick_tutorial/debugtoolbar.rst
index 90750c633..2607c83f2 100644
--- a/docs/quick_tutorial/debugtoolbar.rst
+++ b/docs/quick_tutorial/debugtoolbar.rst
@@ -4,111 +4,129 @@
04: Easier Development with ``debugtoolbar``
============================================
-Error-handling and introspection using the ``pyramid_debugtoolbar``
-add-on.
+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.
+
+ .. code-block:: bash
+
+ cd ..; cp -r ini debugtoolbar; cd debugtoolbar
- .. code-block:: bash
+#. Add ``pyramid_debugtoolbar`` to our project's dependencies in ``setup.py`` as a :term:`Setuptools` "extra" for development:
- $ cd ..; cp -r ini debugtoolbar; cd debugtoolbar
- $ $VENV/bin/python setup.py develop
- $ $VENV/bin/easy_install pyramid_debugtoolbar
+ .. literalinclude:: debugtoolbar/setup.py
+ :language: python
+ :linenos:
+ :emphasize-lines: 10-16, 20-22
-#. Our ``debugtoolbar/development.ini`` gets a configuration entry for
- ``pyramid.includes``:
+#. Install our project and its newly added dependency.
+ Note that we use the extra specifier ``[dev]`` to install development requirements and surround it and the period with double quote marks.
- .. literalinclude:: debugtoolbar/development.ini
- :language: ini
- :linenos:
+ .. code-block:: bash
-#. Run the WSGI application with:
+ $VENV/bin/pip install -e ".[dev]"
- .. code-block:: bash
+#. Our ``debugtoolbar/development.ini`` gets a configuration entry for ``pyramid.includes``:
- $ $VENV/bin/pserve development.ini --reload
+ .. literalinclude:: debugtoolbar/development.ini
+ :language: ini
+ :linenos:
+
+#. Run the WSGI application with:
+
+ .. code-block:: bash
+
+ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in your browser.
+ See the handy toolbar on the right.
-#. Open http://localhost:6543/ in your browser. See the handy
- toolbar on the right.
Analysis
========
-``pyramid_debugtoolbar`` is a full-fledged Python package,
-available on PyPI just like thousands of other Python packages. Thus we
-start by installing the ``pyramid_debugtoolbar`` package into our
-virtual environment using normal Python package installation commands.
-
-The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on,
-which means we need to include its add-on configuration into our web
-application. We could do this with imperative configuration in
-``tutorial/__init__.py`` by using ``config.include``. Pyramid also
-supports wiring in add-on configuration via our ``development.ini``
-using ``pyramid.includes``. We use this to load the configuration for
-the debugtoolbar.
-
-You'll now see an attractive button on the right side of
-your browser, which you may click to provide introspective access to debugging
-information in a new browser tab. Even better, if your web application
-generates an error,
-you will see a nice traceback on the screen. When you want to disable
-this toolbar, no need to change code: you can remove it from
-``pyramid.includes`` in the relevant ``.ini`` configuration file (thus
-showing why configuration files are handy.)
-
-Note injects a small amount of html/css into your app just before the closing
-``</body>`` tag in order to display itself. If you
-start to experience otherwise inexplicable client-side weirdness, you can shut
-it off by commenting out the ``pyramid_debugtoolbar`` line in
-``pyramid.includes`` temporarily.
+``pyramid_debugtoolbar`` is a full-fledged Python package, available on PyPI
+just like thousands of other Python packages. Thus we start by installing the
+``pyramid_debugtoolbar`` package into our virtual environment using normal
+Python package installation commands.
+
+The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on, which
+means we need to include its add-on configuration into our web application. We
+could do this with imperative configuration in ``tutorial/__init__.py`` by
+using ``config.include``. Pyramid also supports wiring in add-on configuration
+via our ``development.ini`` using ``pyramid.includes``. We use this to load the
+configuration for the debugtoolbar.
+
+You'll now see an attractive 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).
+
+Note that the toolbar injects a small amount of HTML/CSS into your app just
+before the closing ``</body>`` tag in order to display itself. If you start to
+experience otherwise inexplicable client-side weirdness, you can shut it off
+by commenting out the ``pyramid_debugtoolbar`` line in ``pyramid.includes``
+temporarily.
+
+Finally we've introduced the concept of :term:`Setuptools` extras.
+These are optional or recommended features that may be installed with an "extras" specifier, in this case, ``dev``.
+The specifier is the name of a key in a Python dictionary, and is surrounded by square brackets when invoked on the command line, for example, .
+The value for the key is a Python list of dependencies.
.. seealso:: See also :ref:`pyramid_debugtoolbar <toolbar:overview>`.
-Extra Credit
+
+Extra credit
============
-# Why don't we add ``pyramid_debugtoolbar`` to the list of
- ``install_requires`` dependencies in ``debugtoolbar/setup.py``?
+#. We added ``pyramid_debugtoolbar`` to the list of ``dev_requires`` dependencies in ``debugtoolbar/setup.py``.
+ We then installed the dependencies via ``pip install -e ".[dev]"`` by virtue of the Setuptools ``extras_require`` value in the Python dictionary.
+ Why did we add them there instead of in the ``requires`` list?
-# Introduce a bug into your application: Change:
+#. Introduce a bug into your application. Change:
- .. code-block:: python
+ .. code-block:: python
- def hello_world(request):
- return Response('<body><h1>Hello World!</h1></body>')
+ def hello_world(request):
+ return Response('<body><h1>Hello World!</h1></body>')
- to:
+ to:
- .. code-block:: python
+ .. code-block:: python
- def hello_world(request):
- return xResponse('<body><h1>Hello World!</h1></body>')
+ def hello_world(request):
+ return xResponse('<body><h1>Hello World!</h1></body>')
- Save, and visit http://localhost:6543/ again. Notice the nice
- traceback display. On the lowest line, click the "screen" icon to the
- right, and try typing the variable names ``request`` and ``Response``.
- What else can you discover?
+ Save, and visit http://localhost:6543/ again.
+ Notice the nice traceback display.
+ On the lowest line, click the "screen" icon to the right, and try typing the variable names ``request`` and ``Response``.
+ What else can you discover?
diff --git a/docs/quick_tutorial/debugtoolbar/development.ini b/docs/quick_tutorial/debugtoolbar/development.ini
index 470d92c57..58d23cff7 100644
--- a/docs/quick_tutorial/debugtoolbar/development.ini
+++ b/docs/quick_tutorial/debugtoolbar/development.ini
@@ -4,37 +4,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/debugtoolbar/setup.py b/docs/quick_tutorial/debugtoolbar/setup.py
index 9997984d3..53bc0f5c7 100644
--- a/docs/quick_tutorial/debugtoolbar/setup.py
+++ b/docs/quick_tutorial/debugtoolbar/setup.py
@@ -1,13 +1,28 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/forms.rst b/docs/quick_tutorial/forms.rst
index e8bc0c8b4..3cd1e26aa 100644
--- a/docs/quick_tutorial/forms.rst
+++ b/docs/quick_tutorial/forms.rst
@@ -1,148 +1,151 @@
.. _qtut_forms:
====================================
-18: Forms and Validation With Deform
+18: Forms and Validation with Deform
====================================
Schema-driven, autogenerated forms with validation.
+
Background
==========
-Modern web applications deal extensively with forms. Developers,
-though, have a wide range of philosophies about how frameworks should
-help them with their forms. As such, Pyramid doesn't directly bundle
-one particular form library. Instead, there are a variety of form
-libraries that are easy to use in Pyramid.
+Modern web applications deal extensively with forms. Developers, though, have a
+wide range of philosophies about how frameworks should help them with their
+forms. As such, Pyramid doesn't directly bundle one particular form library.
+Instead there are a variety of form libraries that are easy to use in Pyramid.
-:ref:`Deform <deform:overview>`
-is one such library. In this step, we introduce Deform for our
-forms and validation. This also gives us the
-:ref:`Colander <colander:overview>` for schemas and validation.
+: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.
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
=====
-#. First we copy the results of the ``view_classes`` step:
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ cd ..; cp -r view_classes forms; cd forms
- .. code-block:: bash
+#. Let's edit ``forms/setup.py`` to declare a dependency on Deform, which in turn pulls in Colander as a dependency:
- $ cd ..; cp -r view_classes forms; cd forms
+ .. literalinclude:: forms/setup.py
+ :emphasize-lines: 6
+ :linenos:
-#. Let's edit ``forms/setup.py`` to declare a dependency on Deform
- (which then pulls in Colander as a dependency:
+#. We can now install our project in development mode:
- .. literalinclude:: forms/setup.py
- :linenos:
+ .. code-block:: bash
-#. We can now install our project in development mode:
+ $VENV/bin/pip install -e .
- .. code-block:: bash
+#. Register a static view in ``forms/tutorial/__init__.py`` for Deform's CSS, JavaScript, etc., as well as our demo wiki page's views:
- $ $VENV/bin/python setup.py develop
+ .. literalinclude:: forms/tutorial/__init__.py
+ :linenos:
-#. Register a static view in ``forms/tutorial/__init__.py`` for
- Deform's CSS/JS etc. as well as our demo wikipage scenario's
- views:
+#. Implement the new views, as well as the form schemas and some dummy data, in ``forms/tutorial/views.py``:
- .. literalinclude:: forms/tutorial/__init__.py
- :linenos:
+ .. literalinclude:: forms/tutorial/views.py
+ :linenos:
-#. Implement the new views, as well as the form schemas and some
- dummy data, in ``forms/tutorial/views.py``:
+#. A template for the top of the "wiki" in ``forms/tutorial/wiki_view.pt``:
- .. literalinclude:: forms/tutorial/views.py
- :linenos:
+ .. literalinclude:: forms/tutorial/wiki_view.pt
+ :language: html
+ :linenos:
-#. A template for the top of the "wiki" in
- ``forms/tutorial/wiki_view.pt``:
+#. Another template for adding/editing in ``forms/tutorial/wikipage_addedit.pt``:
- .. literalinclude:: forms/tutorial/wiki_view.pt
- :language: html
- :linenos:
+ .. literalinclude:: forms/tutorial/wikipage_addedit.pt
+ :language: html
+ :linenos:
-#. Another template for adding/editing in
- ``forms/tutorial/wikipage_addedit.pt``:
+#. Add a template at ``forms/tutorial/wikipage_view.pt`` for viewing a wiki page:
- .. literalinclude:: forms/tutorial/wikipage_addedit.pt
- :language: html
- :linenos:
+ .. literalinclude:: forms/tutorial/wikipage_view.pt
+ :language: html
+ :linenos:
-#. Finally, a template at ``forms/tutorial/wikipage_view.pt``
- for viewing a wiki page:
+#. Our tests in ``forms/tutorial/tests.py`` don't run, so let's modify them:
- .. literalinclude:: forms/tutorial/wikipage_view.pt
- :language: html
- :linenos:
+ .. literalinclude:: forms/tutorial/tests.py
+ :linenos:
-#. Run your Pyramid application with:
+#. Run the tests:
- .. code-block:: bash
+ .. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pytest tutorial/tests.py -q
+ ..
+ 6 passed in 0.81 seconds
-#. Open http://localhost:6543/ in a browser.
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in a browser.
Analysis
========
-This step helps illustrate the utility of asset specifications for
-static assets. We have an outside package called Deform with static
-assets which need to be published. We don't have to know where on disk
-it is located. We point at the package, then the path inside the package.
-
-We just need to include a call to ``add_static_view`` to make that
-directory available at a URL. For Pyramid-specific pages,
-Pyramid provides a facility (``config.include()``) which even makes
-that unnecessary for consumers of a package. (Deform is not specific to
-Pyramid.)
-
-Our forms have rich widgets which need the static CSS and JS just
-mentioned. Deform has a :term:`resource registry` which allows widgets
-to specify which JS and CSS are needed. Our ``wikipage_addedit.pt``
-template shows how we iterated over that data to generate markup that
-includes the needed resources.
-
-Our add and edit views use a pattern called *self-posting forms*.
-Meaning, the same URL is used to ``GET`` the form as is used to
-``POST`` the form. The route, the view, and the template are the same
-whether you are walking up to it the first time or you clicked a button.
-
-Inside the view we do ``if 'submit' in self.request.params:`` to see if
-this form was a ``POST`` where the user clicked on a particular button
+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.)
+
+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 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
``<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.
diff --git a/docs/quick_tutorial/forms/development.ini b/docs/quick_tutorial/forms/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/forms/development.ini
+++ b/docs/quick_tutorial/forms/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/forms/setup.py b/docs/quick_tutorial/forms/setup.py
index 361ade013..0e9ea72bc 100644
--- a/docs/quick_tutorial/forms/setup.py
+++ b/docs/quick_tutorial/forms/setup.py
@@ -1,15 +1,32 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
+ 'deform',
'pyramid',
'pyramid_chameleon',
- 'deform'
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/forms/tutorial/tests.py b/docs/quick_tutorial/forms/tutorial/tests.py
index 5a2c40904..f0e39aa38 100644
--- a/docs/quick_tutorial/forms/tutorial/tests.py
+++ b/docs/quick_tutorial/forms/tutorial/tests.py
@@ -34,3 +34,33 @@ class TutorialFunctionalTests(unittest.TestCase):
def test_home(self):
res = self.testapp.get('/', status=200)
self.assertIn(b'<title>Wiki: View</title>', res.body)
+
+ def test_add_page(self):
+ res = self.testapp.get('/add', status=200)
+ self.assertIn(b'<h1>Wiki</h1>', res.body)
+
+ def test_edit_page(self):
+ res = self.testapp.get('/101/edit', status=200)
+ self.assertIn(b'<h1>Wiki</h1>', res.body)
+
+ def test_post_wiki(self):
+ self.testapp.post('/add', {
+ "title": "New Title",
+ "body": "<p>New Body</p>",
+ "submit": "submit"
+ }, status=302)
+
+ res = self.testapp.get('/103', status=200)
+ self.assertIn(b'<h1>New Title</h1>', res.body)
+ self.assertIn(b'<p>New Body</p>', res.body)
+
+ def test_edit_wiki(self):
+ self.testapp.post('/102/edit', {
+ "title": "New Title",
+ "body": "<p>New Body</p>",
+ "submit": "submit"
+ }, status=302)
+
+ res = self.testapp.get('/102', status=200)
+ self.assertIn(b'<h1>New Title</h1>', res.body)
+ self.assertIn(b'<p>New Body</p>', res.body)
diff --git a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
index 3292dfd90..25bab04d0 100644
--- a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
+++ b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
@@ -2,10 +2,21 @@
<html lang="en">
<head>
<title>WikiPage: Add/Edit</title>
+ <link rel="stylesheet"
+ href="${request.static_url('deform:static/css/bootstrap.min.css')}"
+ type="text/css" media="screen" charset="utf-8"/>
+ <link rel="stylesheet"
+ href="${request.static_url('deform:static/css/form.css')}"
+ type="text/css"/>
<tal:block tal:repeat="reqt view.reqts['css']">
<link rel="stylesheet" type="text/css"
href="${request.static_url(reqt)}"/>
</tal:block>
+ <script src="${request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}"
+ type="text/javascript"></script>
+ <script src="${request.static_url('deform:static/scripts/bootstrap.min.js')}"
+ type="text/javascript"></script>
+
<tal:block tal:repeat="reqt view.reqts['js']">
<script src="${request.static_url(reqt)}"
type="text/javascript"></script>
diff --git a/docs/quick_tutorial/functional_testing.rst b/docs/quick_tutorial/functional_testing.rst
index 205ddf5cb..054d03761 100644
--- a/docs/quick_tutorial/functional_testing.rst
+++ b/docs/quick_tutorial/functional_testing.rst
@@ -6,65 +6,77 @@
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 <https://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.
-WebTest is a Python package that does functional testing. With WebTest
-you can write tests which simulate a full HTTP request against a WSGI
-application, then test the information in the response. For speed
-purposes, WebTest skips the setup/teardown of an actual HTTP server,
-providing tests that run fast enough to be part of TDD.
Objectives
==========
-- Write a test which checks the contents of the returned HTML
+- 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.
+
+ .. code-block:: bash
- .. code-block:: bash
+ cd ..; cp -r unit_testing functional_testing; cd functional_testing
- $ cd ..; cp -r unit_testing functional_testing; cd functional_testing
- $ $VENV/bin/python setup.py develop
- $ $VENV/bin/easy_install webtest
+#. Add ``webtest`` to our project's dependencies in ``setup.py`` as a :term:`Setuptools` "extra":
-#. Let's extend ``unit_testing/tutorial/tests.py`` to include a
- functional test:
+ .. literalinclude:: functional_testing/setup.py
+ :language: python
+ :linenos:
+ :emphasize-lines: 16
- .. literalinclude:: functional_testing/tutorial/tests.py
- :linenos:
+#. Install our project and its newly added dependency.
+ Note that we use the extra specifier ``[dev]`` to install testing requirements for development and surround it and the period with double quote marks.
-#. Now run the tests:
+ .. code-block:: bash
- .. code-block:: bash
+ $VENV/bin/pip install -e ".[dev]"
+#. Let's extend ``functional_testing/tutorial/tests.py`` to include a functional test:
- $ $VENV/bin/nosetests tutorial
- .
- ----------------------------------------------------------------------
- Ran 2 tests in 0.141s
+ .. literalinclude:: functional_testing/tutorial/tests.py
+ :linenos:
+
+ Be sure this file is not executable, or ``pytest`` may not include your tests.
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+ $VENV/bin/pytest tutorial/tests.py -q
+ ..
+ 2 passed in 0.25 seconds
- 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''``? \ No newline at end of file
+#. Why do our functional tests use ``b''``?
diff --git a/docs/quick_tutorial/functional_testing/development.ini b/docs/quick_tutorial/functional_testing/development.ini
index 470d92c57..58d23cff7 100644
--- a/docs/quick_tutorial/functional_testing/development.ini
+++ b/docs/quick_tutorial/functional_testing/development.ini
@@ -4,37 +4,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/functional_testing/setup.py b/docs/quick_tutorial/functional_testing/setup.py
index 9997984d3..a0fa8217c 100644
--- a/docs/quick_tutorial/functional_testing/setup.py
+++ b/docs/quick_tutorial/functional_testing/setup.py
@@ -1,13 +1,30 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/hello_world.rst b/docs/quick_tutorial/hello_world.rst
index 1a9ba4c9d..161f07ac8 100644
--- a/docs/quick_tutorial/hello_world.rst
+++ 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 ``setup.py``, 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 were all the rage, until the next shiny thing came along. "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
=====
@@ -49,66 +49,65 @@ Steps
.. code-block:: bash
- $ mkdir hello_world; cd hello_world
+ cd ~/projects/quick_tutorial; mkdir hello_world; cd hello_world
#. Copy the following into ``hello_world/app.py``:
.. literalinclude:: hello_world/app.py
- :linenos:
+ :linenos:
#. Run the application:
.. code-block:: bash
- $ $VENV/bin/python app.py
+ $VENV/bin/python app.py
#. Open http://localhost:6543/ in your browser.
+
Analysis
========
-New to Python web programming? If so, some lines in module merit
+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` in a :term:`context manager` 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-7*. 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 Tutorial*.
-As shown in this example, the :term:`configurator` plays a
-central role in Pyramid development. Building an application from
-loosely-coupled parts via :ref:`configuration_narr` is a
-central idea in Pyramid, one that we will revisit regularly in this
-*Quick Tour*.
-Extra Credit
+Extra credit
============
#. Why do we do this:
.. code-block:: python
- print('Incoming request')
+ print('Incoming request')
...instead of:
.. code-block:: python
- print 'Incoming request'
+ print 'Incoming request'
#. 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?
diff --git a/docs/quick_tutorial/hello_world/app.py b/docs/quick_tutorial/hello_world/app.py
index 0a95f9ad3..ff0b950d3 100644
--- a/docs/quick_tutorial/hello_world/app.py
+++ b/docs/quick_tutorial/hello_world/app.py
@@ -1,4 +1,4 @@
-from wsgiref.simple_server import make_server
+from waitress import serve
from pyramid.config import Configurator
from pyramid.response import Response
@@ -9,9 +9,8 @@ def hello_world(request):
if __name__ == '__main__':
- config = Configurator()
- config.add_route('hello', '/')
- config.add_view(hello_world, route_name='hello')
- app = config.make_wsgi_app()
- server = make_server('0.0.0.0', 6543, app)
- server.serve_forever()
+ with Configurator() as config:
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0', port=6543)
diff --git a/docs/quick_tutorial/index.rst b/docs/quick_tutorial/index.rst
index 9373fe38a..b5b7b3313 100644
--- a/docs/quick_tutorial/index.rst
+++ b/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
========
@@ -19,7 +19,7 @@ Contents
requirements
tutorial_approach
- scaffolds
+ cookiecutters
hello_world
package
ini
diff --git a/docs/quick_tutorial/ini.rst b/docs/quick_tutorial/ini.rst
index 3402c50e8..0bb7da1ba 100644
--- a/docs/quick_tutorial/ini.rst
+++ b/docs/quick_tutorial/ini.rst
@@ -7,101 +7,100 @@
Use Pyramid's ``pserve`` command with a ``.ini`` configuration file for
simpler, better application running.
+
Background
==========
-Pyramid has a first-class concept of
-:ref:`configuration <configuration_narr>` distinct from code.
-This approach is optional, but its presence makes it distinct from
-other Python web frameworks. It taps into Python's ``setuptools``
-library, which establishes conventions for how Python projects can be
-installed and provide "entry points". Pyramid uses an entry point to
-let a Pyramid application it where to find the WSGI app.
+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 :term:`Setuptools` library, which establishes conventions for installing and providing ":term:`entry point`\ s" for Python projects.
+Pyramid uses an :term:`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 :term:`entry point` telling Pyramid the location of the WSGI app.
+
+- Create an application driven by an ``.ini`` file.
-- Create an application driven by a ``.ini`` file
+- Start the application with Pyramid's ``pserve`` command.
-- Startup the application with Pyramid's ``pserve`` command
+- Move code into the package's ``__init__.py``.
-- Move code into the package's ``__init__.py``
Steps
=====
-#. First we copy the results of the previous step:
+#. First we copy the results of the previous step:
- .. code-block:: bash
+ .. code-block:: bash
- $ cd ..; cp -r package ini; cd ini
+ 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 :term:`Setuptools` :term:`entry point` in the ``setup()`` function:
- .. literalinclude:: ini/setup.py
- :linenos:
+ .. literalinclude:: ini/setup.py
+ :linenos:
+ :emphasize-lines: 13-17
-#. 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
+ .. code-block:: bash
- $ $VENV/bin/python setup.py develop
+ $VENV/bin/pip install -e .
-#. Let's make a file ``ini/development.ini`` for our configuration:
+#. Let's make a file ``ini/development.ini`` for our configuration:
- .. literalinclude:: ini/development.ini
- :language: ini
- :linenos:
+ .. literalinclude:: ini/development.ini
+ :language: ini
+ :linenos:
-#. We can refactor our startup code from the previous step's ``app.py``
- into ``ini/tutorial/__init__.py``:
+#. We can refactor our startup code from the previous step's ``app.py`` into ``ini/tutorial/__init__.py``:
- .. literalinclude:: ini/tutorial/__init__.py
- :linenos:
+ .. literalinclude:: ini/tutorial/__init__.py
+ :linenos:
-#. Now that ``ini/tutorial/app.py`` isn't used, let's remove it:
+#. Now that ``ini/tutorial/app.py`` isn't used, let's remove it:
- .. code-block:: bash
+ .. code-block:: bash
- $ rm tutorial/app.py
+ rm tutorial/app.py
-#. Run your Pyramid application with:
+#. Run your Pyramid application with:
- .. code-block:: bash
+ .. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pserve development.ini --reload
-#. Open http://localhost:6543/.
+#. Open http://localhost:6543/.
Analysis
========
-Our ``development.ini`` file is read by ``pserve`` and serves to
-bootstrap our application. Processing then proceeds as described in
-the Pyramid chapter on
+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-10)
- for the project "main" entry point of ``tutorial:main``
+- The projects's ``setup.py`` has defined an :term:`entry point` (lines 10-13) for the project's "main" :term:`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 ``waitress`` which we specified in
+ ``tutorial/setup.py`` and was installed in the :doc:`requirements` step at the start of this tutorial. It also wires up the *port number*:
+ ``listen = localhost:6543`` tells ``waitress`` to listen on host
+ ``localhost`` at port ``6543``.
+
+ .. note:: Running the command ``$VENV/bin/pip install -e .`` will check for previously installed packages in our virtual environment that are specified in our package's ``setup.py`` file, then install our package in editable mode, installing any requirements that were not previously installed. If a requirement was manually installed previously on the command line or otherwise, in this case Waitress, then ``$VENV/bin/pip install -e .`` will merely check that it is installed and move on.
- *Configuring Python logging*. Pyramid uses Python standard logging, which
needs a number of configuration values. The ``.ini`` serves this function.
@@ -109,36 +108,32 @@ The ``.ini`` file is also used for two other functions:
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
+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 :term:`entry point` in ``setup.py`` didn't mention ``__init__.py`` when it declared ``tutorial:main`` function. Why not?
+
+#. What is the purpose of ``**settings``? What does the ``**`` signify?
.. seealso::
:ref:`project_narr`,
- :ref:`scaffolding_chapter`,
+ :ref:`cookiecutters`,
:ref:`what_is_this_pserve_thing`,
:ref:`environment_chapter`,
:ref:`paste_chapter`
-
-Extra Credit
-============
-
-#. What is the purpose of ``**settings``? What does the ``**`` signify?
diff --git a/docs/quick_tutorial/ini/development.ini b/docs/quick_tutorial/ini/development.ini
index ca7d9bf81..5361188a3 100644
--- a/docs/quick_tutorial/ini/development.ini
+++ b/docs/quick_tutorial/ini/development.ini
@@ -2,37 +2,5 @@
use = egg:tutorial
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/ini/setup.py b/docs/quick_tutorial/ini/setup.py
index 9997984d3..f1d06fe75 100644
--- a/docs/quick_tutorial/ini/setup.py
+++ b/docs/quick_tutorial/ini/setup.py
@@ -1,13 +1,18 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+setup(
+ name='tutorial',
+ install_requires=requires,
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/jinja2.rst b/docs/quick_tutorial/jinja2.rst
index 2f1e295dd..ed9acd955 100644
--- a/docs/quick_tutorial/jinja2.rst
+++ b/docs/quick_tutorial/jinja2.rst
@@ -4,94 +4,98 @@
12: Templating With ``jinja2``
==============================
-We just said Pyramid doesn't prefer one templating language over
-another. Time to prove it. Jinja2 is a popular templating system,
-used in Flask and modelled after Django's templates. Let's add
-``pyramid_jinja2``, a Pyramid :term:`add-on` which enables Jinja2 as a
-:term:`renderer` in our Pyramid applications.
+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 installing the ``pyramid_jinja2``
- add-on, the copying the ``view_class`` step's directory:
+#. In this step let's start by copying the ``view_class`` step's directory from a few steps ago.
+
+ .. code-block:: bash
+
+ cd ..; cp -r view_classes jinja2; cd jinja2
+
+#. Add ``pyramid_jinja2`` to our project's dependencies in ``setup.py``:
+
+ .. literalinclude:: jinja2/setup.py
+ :language: python
+ :linenos:
+ :emphasize-lines: 8
- .. code-block:: bash
+#. Install our project and its newly added dependency.
- $ cd ..; cp -r view_classes jinja2; cd jinja2
- $ $VENV/bin/python setup.py develop
- $ $VENV/bin/easy_install pyramid_jinja2
+ .. code-block:: bash
-#. We need to include ``pyramid_jinja2`` in
- ``jinja2/tutorial/__init__.py``:
+ $VENV/bin/pip install -e .
- .. literalinclude:: jinja2/tutorial/__init__.py
- :linenos:
+#. We need to include ``pyramid_jinja2`` in ``jinja2/tutorial/__init__.py``:
-#. Our ``jinja2/tutorial/views.py`` simply changes its ``renderer``:
+ .. literalinclude:: jinja2/tutorial/__init__.py
+ :linenos:
- .. literalinclude:: jinja2/tutorial/views.py
- :linenos:
+#. Our ``jinja2/tutorial/views.py`` simply changes its ``renderer``:
-#. Add ``jinja2/tutorial/home.jinja2`` as a template:
+ .. literalinclude:: jinja2/tutorial/views.py
+ :linenos:
- .. literalinclude:: jinja2/tutorial/home.jinja2
- :language: html
+#. Add ``jinja2/tutorial/home.jinja2`` as a template:
-#. Get the ``pyramid.includes`` into the functional test setup in
- ``jinja2/tutorial/tests.py``:
+ .. literalinclude:: jinja2/tutorial/home.jinja2
+ :language: html
- .. literalinclude:: jinja2/tutorial/tests.py
- :linenos:
+#. Now run the tests:
-#. Now run the tests:
+ .. code-block:: bash
- .. code-block:: bash
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 4 passed in 0.40 seconds
- $ $VENV/bin/nosetests tutorial
+#. Run your Pyramid application with:
-#. Run your Pyramid application with:
+ .. code-block:: bash
- .. code-block:: bash
+ $VENV/bin/pserve development.ini --reload
- $ $VENV/bin/pserve development.ini --reload
+#. Open http://localhost:6543/ in your browser.
-#. 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.
-Our functional tests don't have ``development.ini`` so they needed the
-``pyramid.includes`` to be setup in the test setup.
-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 we 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>`
diff --git a/docs/quick_tutorial/jinja2/development.ini b/docs/quick_tutorial/jinja2/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/jinja2/development.ini
+++ b/docs/quick_tutorial/jinja2/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/jinja2/setup.py b/docs/quick_tutorial/jinja2/setup.py
index 9997984d3..ea2c59045 100644
--- a/docs/quick_tutorial/jinja2/setup.py
+++ b/docs/quick_tutorial/jinja2/setup.py
@@ -1,13 +1,32 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_jinja2',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/jinja2/tutorial/home.jinja2 b/docs/quick_tutorial/jinja2/tutorial/home.jinja2
index 975323169..20d33b733 100644
--- a/docs/quick_tutorial/jinja2/tutorial/home.jinja2
+++ b/docs/quick_tutorial/jinja2/tutorial/home.jinja2
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: {{ name }}</title>
+ <title>Quick Tutorial: {{ name }}</title>
</head>
<body>
<h1>Hi {{ name }}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/jinja2/tutorial/tests.py b/docs/quick_tutorial/jinja2/tutorial/tests.py
index 0b22946f3..4381235ec 100644
--- a/docs/quick_tutorial/jinja2/tutorial/tests.py
+++ b/docs/quick_tutorial/jinja2/tutorial/tests.py
@@ -30,13 +30,7 @@ class TutorialViewTests(unittest.TestCase):
class TutorialFunctionalTests(unittest.TestCase):
def setUp(self):
from tutorial import main
-
- settings = {
- 'pyramid.includes': [
- 'pyramid_jinja2'
- ]
- }
- app = main({}, **settings)
+ app = main({})
from webtest import TestApp
self.testapp = TestApp(app)
diff --git a/docs/quick_tutorial/json.rst b/docs/quick_tutorial/json.rst
index aa789d833..44d1de8cb 100644
--- a/docs/quick_tutorial/json.rst
+++ b/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
=====
@@ -30,32 +31,33 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes json; cd json
- $ $VENV/bin/python setup.py develop
+ 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:
+ :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:
+ :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:
+ :linenos:
#. Run the tests:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
+ $VENV/bin/pytest tutorial/tests.py -q
+ .....
+ 5 passed in 0.47 seconds
+
#. Run your Pyramid application with:
@@ -63,40 +65,39 @@ Steps
$ $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
diff --git a/docs/quick_tutorial/json/development.ini b/docs/quick_tutorial/json/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/json/development.ini
+++ b/docs/quick_tutorial/json/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/json/setup.py b/docs/quick_tutorial/json/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/json/setup.py
+++ b/docs/quick_tutorial/json/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/json/tutorial/__init__.py b/docs/quick_tutorial/json/tutorial/__init__.py
index 6652544c3..8c3ea97ff 100644
--- a/docs/quick_tutorial/json/tutorial/__init__.py
+++ b/docs/quick_tutorial/json/tutorial/__init__.py
@@ -6,6 +6,6 @@ def main(global_config, **settings):
config.include('pyramid_chameleon')
config.add_route('home', '/')
config.add_route('hello', '/howdy')
- config.add_route('hello_json', 'howdy.json')
+ config.add_route('hello_json', '/howdy.json')
config.scan('.views')
- return config.make_wsgi_app() \ No newline at end of file
+ return config.make_wsgi_app()
diff --git a/docs/quick_tutorial/json/tutorial/home.pt b/docs/quick_tutorial/json/tutorial/home.pt
index a0cc08e7a..fd4ef8764 100644
--- a/docs/quick_tutorial/json/tutorial/home.pt
+++ b/docs/quick_tutorial/json/tutorial/home.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/logging.rst b/docs/quick_tutorial/logging.rst
index 855ded59f..f4a368bfa 100644
--- a/docs/quick_tutorial/logging.rst
+++ b/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 project
+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``, 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
=====
@@ -34,44 +36,49 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes logging; cd logging
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r view_classes logging; cd logging
+ $VENV/bin/pip install -e .
#. Extend ``logging/tutorial/views.py`` to log a message:
.. literalinclude:: logging/tutorial/views.py
- :linenos:
+ :linenos:
+
+#. Finally let's edit ``development.ini`` configuration file to enable logging
+ for our Pyramid application:
+
+ .. literalinclude:: logging/development.ini
+ :language: ini
#. Make sure the tests still pass:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 4 passed in 0.41 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $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
========
-Our ``development.ini`` configuration file wires up Python standard
-logging for our Pyramid application:
-
-.. literalinclude:: logging/development.ini
- :language: ini
+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:
-In this, our ``tutorial`` Python package is setup as a logger
-and configured to log messages at a ``DEBUG`` or higher level. When you
-visit http://localhost:6543 your console will now show::
+.. code-block:: text
- 2013-08-09 10:42:42,968 DEBUG [tutorial.views][MainThread] In home view
+ 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.
diff --git a/docs/quick_tutorial/logging/development.ini b/docs/quick_tutorial/logging/development.ini
index 62e0c5123..ff470acdb 100644
--- a/docs/quick_tutorial/logging/development.ini
+++ b/docs/quick_tutorial/logging/development.ini
@@ -5,9 +5,8 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
+use = egg:waitress#main
+listen = localhost:6543
# Begin logging configuration
diff --git a/docs/quick_tutorial/logging/setup.py b/docs/quick_tutorial/logging/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/logging/setup.py
+++ b/docs/quick_tutorial/logging/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/logging/tutorial/home.pt b/docs/quick_tutorial/logging/tutorial/home.pt
index a0cc08e7a..fd4ef8764 100644
--- a/docs/quick_tutorial/logging/tutorial/home.pt
+++ b/docs/quick_tutorial/logging/tutorial/home.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes.rst b/docs/quick_tutorial/more_view_classes.rst
index 9cc4cc520..b7e724dcf 100644
--- a/docs/quick_tutorial/more_view_classes.rst
+++ b/docs/quick_tutorial/more_view_classes.rst
@@ -6,176 +6,192 @@
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 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.
-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.
+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, and so on. 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, etc. 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 stated and logic between views and templates via the view class
Steps
=====
-#. First we copy the results of the previous step:
+#. First we copy the results of the ``templating`` step:
.. code-block:: bash
- $ cd ..; cp -r templating more_view_classes; cd more_view_classes
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r templating more_view_classes; cd more_view_classes
+ $VENV/bin/pip install -e .
#. Our route in ``more_view_classes/tutorial/__init__.py`` needs some
replacement patterns:
.. literalinclude:: more_view_classes/tutorial/__init__.py
- :linenos:
+ :linenos:
#. Our ``more_view_classes/tutorial/views.py`` now has a view class with
several views:
.. literalinclude:: more_view_classes/tutorial/views.py
- :linenos:
+ :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
+ :language: html
#. Ditto for our other view from the previous section at
``more_view_classes/tutorial/hello.pt``:
.. literalinclude:: more_view_classes/tutorial/hello.pt
- :language: html
+ :language: html
#. We have an edit view that also needs a template at
``more_view_classes/tutorial/edit.pt``:
.. literalinclude:: more_view_classes/tutorial/edit.pt
- :language: html
+ :language: html
#. And finally the delete view's template at
``more_view_classes/tutorial/delete.pt``:
.. literalinclude:: more_view_classes/tutorial/delete.pt
- :language: html
+ :language: html
+
+#. Our tests in ``more_view_classes/tutorial/tests.py`` fail, so let's modify
+ them:
+
+ .. literalinclude:: more_view_classes/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+ $VENV/bin/pytest tutorial/tests.py -q
+ ..
+ 2 passed in 0.40 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $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 to
+In this step we show, using the following information as criteria, how to
decide which view to use:
- Method of the HTTP request (``GET``, ``POST``, etc.)
- 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 hardcoded the URLs, such as:
+
+.. code-block:: html
-As a note, we made a switch in our templates on how we generate URLs.
-We previously hardcode the URLs, such as::
+ <a href="/howdy/jane/doe">Howdy</a>
- <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',
+ <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
- URL Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_
+ URL Dispatch <https://web.archive.org/web/20170131192830/http://www.plope.com/weird_pyramid_urldispatch>`_
diff --git a/docs/quick_tutorial/more_view_classes/development.ini b/docs/quick_tutorial/more_view_classes/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/more_view_classes/development.ini
+++ b/docs/quick_tutorial/more_view_classes/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/more_view_classes/setup.py b/docs/quick_tutorial/more_view_classes/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/more_view_classes/setup.py
+++ b/docs/quick_tutorial/more_view_classes/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/delete.pt b/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
index 67cc8bf09..7bd4d3b0d 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${page_title}</title>
+ <title>Quick Tutorial: ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/edit.pt b/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
index 1bd204065..523a4ce5d 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+ <title>Quick Tutorial: ${view.view_name} - ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
<p>You submitted <code>${new_name}</code></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/hello.pt b/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
index 8a39aed09..40b00bfe4 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+ <title>Quick Tutorial: ${view.view_name} - ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
@@ -13,4 +13,4 @@
<input type="submit" name="form.delete" value="Delete"/>
</form>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/home.pt b/docs/quick_tutorial/more_view_classes/tutorial/home.pt
index fa9016705..fa0436f7e 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/home.pt
+++ b/docs/quick_tutorial/more_view_classes/tutorial/home.pt
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+ <title>Quick Tutorial: ${view.view_name} - ${page_title}</title>
</head>
<body>
<h1>${view.view_name} - ${page_title}</h1>
@@ -9,4 +9,4 @@
<p>Go to the <a href="${request.route_url('hello', first='jane',
last='doe')}">form</a>.</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/views.py b/docs/quick_tutorial/more_view_classes/tutorial/views.py
index 635de0520..156e468a9 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/views.py
+++ b/docs/quick_tutorial/more_view_classes/tutorial/views.py
@@ -5,7 +5,7 @@ from pyramid.view import (
@view_defaults(route_name='hello')
-class TutorialViews:
+class TutorialViews(object):
def __init__(self, request):
self.request = request
self.view_name = 'TutorialViews'
@@ -25,13 +25,13 @@ class TutorialViews:
def hello(self):
return {'page_title': 'Hello View'}
- # Posting to /home via the "Edit" submit button
+ # Posting to /howdy/first/last via the "Edit" submit button
@view_config(request_method='POST', renderer='edit.pt')
def edit(self):
new_name = self.request.params['new_name']
return {'page_title': 'Edit View', 'new_name': new_name}
- # Posting to /home via the "Delete" submit button
+ # Posting to /howdy/first/last via the "Delete" submit button
@view_config(request_method='POST', request_param='form.delete',
renderer='delete.pt')
def delete(self):
diff --git a/docs/quick_tutorial/package.rst b/docs/quick_tutorial/package.rst
index 8fb052d5b..ae82fef03 100644
--- a/docs/quick_tutorial/package.rst
+++ b/docs/quick_tutorial/package.rst
@@ -3,50 +3,48 @@
============================================
Most modern Python development is done using Python packages, an approach
-Pyramid puts to good use. In this step we re-do "Hello World" as a
-minimum Python package inside a minimum Python project.
+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 (muddled, but improving) toolchain oriented
-around a ``setup.py`` file for a
-`setuptools project <http://pythonhosted.org/setuptools/setuptools.html>`_.
-Explaining it all in this
-tutorial will induce madness. For this tutorial, this is all you need to
-know:
+Packages can be bundled up, made available for installation, and installed
+through a toolchain oriented around a ``setup.py`` file. For this tutorial,
+this is all you need to know:
-- We will have a directory for each tutorial step as a setuptools *project*
+- We will have a directory for each tutorial step as a *project*.
-- This project will contain a ``setup.py`` which injects the features
- of the setuptool's project machinery into the directory
+- This project will contain a ``setup.py`` which injects the features of the
+ project machinery into the directory.
- In this project we will make a ``tutorial`` subdirectory into a Python
- *package* using an ``__init__.py`` Python module file
+ *package* using an ``__init__.py`` Python module file.
-- We will run ``python setup.py develop`` to install our project in
- development mode
+- We will run ``pip install -e .`` to install our project in development mode.
In summary:
-- You'll do your development in a Python *package*
+- You'll do your development in a Python *package*.
+
+- That package will be part of a *project*.
-- That package will be part of a setuptools *project*
Objectives
==========
-- Make a Python "package" directory with an ``__init__.py``
+- Make a Python "package" directory with an ``__init__.py``.
-- Get a minimum Python "project" in place by making a ``setup.py``
+- Get a minimum Python "project" in place by making a ``setup.py``.
+
+- Install our ``tutorial`` project in development mode.
-- Install our ``tutorial`` project in development mode
Steps
=====
@@ -55,19 +53,19 @@ Steps
.. code-block:: bash
- $ cd ..; mkdir package; cd package
+ cd ..; mkdir package; cd package
#. In ``package/setup.py``, enter the following:
.. literalinclude:: package/setup.py
-#. Make the new project installed for development then make a directory
- for the actual code:
+#. Make the new project installed for development then make a directory for the
+ actual code:
.. code-block:: bash
- $ $VENV/bin/python setup.py develop
- $ mkdir tutorial
+ $VENV/bin/pip install -e .
+ mkdir tutorial
#. Enter the following into ``package/tutorial/__init__.py``:
@@ -81,31 +79,33 @@ Steps
.. code-block:: bash
- $ $VENV/bin/python tutorial/app.py
+ $VENV/bin/python tutorial/app.py
#. Open http://localhost:6543/ in your browser.
+
Analysis
========
-Python packages give us an organized unit of project development.
-Python projects, via ``setup.py``, gives us special features when
-our package is installed (in this case, in local development mode.)
+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 re-typing.
+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>`,
- `setuptools Entry Points <http://pythonhosted.org/setuptools/pkg_resources.html#entry-points>`_
+.. seealso:: :ref:`Python Packages <python:tut-packages>` and `Working in
+ "Development Mode"
+ <https://packaging.python.org/tutorials/installing-packages/#installing-from-a-local-src-tree>`_.
diff --git a/docs/quick_tutorial/package/setup.py b/docs/quick_tutorial/package/setup.py
index bcfcfa684..910c552ad 100644
--- a/docs/quick_tutorial/package/setup.py
+++ b/docs/quick_tutorial/package/setup.py
@@ -1,9 +1,13 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
-) \ No newline at end of file
+setup(
+ name='tutorial',
+ install_requires=requires,
+)
diff --git a/docs/quick_tutorial/package/tutorial/app.py b/docs/quick_tutorial/package/tutorial/app.py
index 210075023..ff0b950d3 100644
--- a/docs/quick_tutorial/package/tutorial/app.py
+++ b/docs/quick_tutorial/package/tutorial/app.py
@@ -1,17 +1,16 @@
-from wsgiref.simple_server import make_server
+from waitress import serve
from pyramid.config import Configurator
from pyramid.response import Response
def hello_world(request):
- print ('Incoming request')
+ print('Incoming request')
return Response('<body><h1>Hello World!</h1></body>')
if __name__ == '__main__':
- config = Configurator()
- config.add_route('hello', '/')
- config.add_view(hello_world, route_name='hello')
- app = config.make_wsgi_app()
- server = make_server('0.0.0.0', 6543, app)
- server.serve_forever() \ No newline at end of file
+ with Configurator() as config:
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0', port=6543)
diff --git a/docs/quick_tutorial/request_response.rst b/docs/quick_tutorial/request_response.rst
index 504803804..f7753f222 100644
--- a/docs/quick_tutorial/request_response.rst
+++ b/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, cookiecutters, 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>`.
-Pyramid has always fit nicely into the existing world of Python web
-development (virtual environments, packaging, scaffolding,
-first to embrace Python 3, etc.) For request handling, Pyramid turned
-to the well-regarded :term:`WebOb` Python library for request and
-response handling. In our example
-above, Pyramid hands ``hello_world`` a ``request`` that is
-:ref:`based on WebOb <webob_chapter>`.
Steps
=====
@@ -40,64 +39,71 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes request_response; cd request_response
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r view_classes request_response; cd request_response
+ $VENV/bin/pip install -e .
#. Simplify the routes in ``request_response/tutorial/__init__.py``:
.. literalinclude:: request_response/tutorial/__init__.py
+ :linenos:
#. We only need one view in ``request_response/tutorial/views.py``:
.. literalinclude:: request_response/tutorial/views.py
+ :linenos:
#. Update the tests in ``request_response/tutorial/tests.py``:
.. literalinclude:: request_response/tutorial/tests.py
+ :linenos:
#. Now run the tests:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
+ $VENV/bin/pytest tutorial/tests.py -q
+ .....
+ 5 passed in 0.30 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $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:
+
+.. code-block:: text
-In this Pyramid view, we get the URL being visited from ``request.url``.
-Also, if you visited http://localhost:6543/plain?name=alice,
-the name is included in the body of the response::
+ URL http://localhost:6543/plain?name=alice with name: alice
- 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>`
diff --git a/docs/quick_tutorial/request_response/development.ini b/docs/quick_tutorial/request_response/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/request_response/development.ini
+++ b/docs/quick_tutorial/request_response/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/request_response/setup.py b/docs/quick_tutorial/request_response/setup.py
index 9997984d3..e9c068a23 100644
--- a/docs/quick_tutorial/request_response/setup.py
+++ b/docs/quick_tutorial/request_response/setup.py
@@ -1,13 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/requirements.rst b/docs/quick_tutorial/requirements.rst
index b5778ea42..a74c07673 100644
--- a/docs/quick_tutorial/requirements.rst
+++ b/docs/quick_tutorial/requirements.rst
@@ -4,79 +4,67 @@
Requirements
============
-Let's get our tutorial environment setup. Most of the setup work is in
-standard Python development practices (install Python,
-make an isolated environment, and setup packaging tools.)
+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
+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.3**. Pyramid fully supports Python 3.2+ and Python 2.6+.
- This tutorial uses **Python 3.3** but runs fine under Python 2.7.
+* **Python 3.7**. Pyramid fully supports Python 3.4+ and Python 2.7+. This
+ tutorial uses **Python 3.7** but runs fine under Python 2.7.
-* **pyvenv**. We believe in virtual environments. For this tutorial,
- we use Python 3.3's built-in solution, the ``pyvenv`` command.
- For Python 2.7, you can install ``virtualenv``.
+* **venv**. We believe in virtual environments. For this tutorial, we use
+ Python 3's built-in solution :term:`venv`. For Python 2.7, you can install
+ :term:`virtualenv`.
-* **setuptools and easy_install**. We use
- `setuptools <https://pypi.python.org/pypi/setuptools/>`_
- and its ``easy_install`` 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
=====
-#. :ref:`install-python-3.3-or-greater`
+#. :ref:`install-python-3`
#. :ref:`create-a-project-directory-structure`
#. :ref:`set-an-environment-variable`
#. :ref:`create-a-virtual-environment`
-#. :ref:`install-setuptools-(python-packaging-tools)`
#. :ref:`install-pyramid`
-.. _install-python-3.3-or-greater:
-Install Python 3.3 or greater
------------------------------
+.. _install-python-3:
-Download the latest standard Python 3.3+ release (not development
-release) from
-`python.org <http://www.python.org/download/releases/>`_. On that page, you
-must click the latest version, then scroll down to the "Downloads" section
-for your operating system.
+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`.
-Windows users should also install the `Python for Windows extensions
-<http://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read the
-``README.txt`` file at the end of the list of builds, and follow its
-directions. Make sure you get the proper 32- or 64-bit build and Python
-version.
-
-Linux users can either use their package manager to install Python 3.3
-or may
-`build Python 3.3 from source
-<http://pyramid.readthedocs.org/en/master/narr/install.html#package-manager-
-method>`_.
+- :ref:`for-macos-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,126 +72,115 @@ method>`_.
Create a project directory structure
------------------------------------
-We will arrive at a directory structure of
-``workspace->project->package``, with our workspace named
-``quick_tutorial``. The following diagram shows how this is structured
-and where our virtual environment will reside:
+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:
-.. figure:: ../_static/directory_structure_pyramid.png
- :alt: Final directory structure
+.. code-block:: text
- Final directory structure.
+ ~
+ └── projects
+ └── quick_tutorial
+ ├── env
+ └── step_one
+ ├── intro
+ │ ├── __init__.py
+ │ └── app.py
+ └── setup.py
-For Linux, the commands to do so are as follows:
+For macOS and Linux, the commands to do so are as follows:
.. code-block:: bash
- # Mac and Linux
- $ cd ~
- $ mkdir -p projects/quick_tutorial
- $ cd projects/quick_tutorial
+ # macOS and Linux
+ cd ~
+ mkdir -p projects/quick_tutorial
+ cd projects/quick_tutorial
For Windows:
-.. code-block:: posh
+.. code-block:: doscon
# Windows
- c:\> cd \
- c:\> mkdir projects\quick_tutorial
- c:\> cd projects\quick_tutorial
+ cd \
+ mkdir projects\quick_tutorial
+ 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 (virtualenvs) 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/env33/
+ # macOS and Linux
+ export VENV=~/projects/quick_tutorial/env
+
+.. code-block:: doscon
# Windows
- # TODO: This command does not work
- c:\> set VENV=c:\projects\quick_tutorial\env33
+ set VENV=c:\projects\quick_tutorial\env
.. _create-a-virtual-environment:
-Create a Virtual Environment
+Create a virtual environment
----------------------------
-.. warning:: The current state of isolated Python environments using
- ``pyvenv`` on Windows is suboptimal in comparison to Mac and Linux. See
- http://stackoverflow.com/q/15981111/95735 for a discussion of the issue
- and `PEP 453 <http://www.python.org/dev/peps/pep-0453/>`_ for a proposed
- resolution.
-
-``pyvenv`` is a tool to create isolated Python 3.3 environments, each
-with its own Python binary and independent set of installed Python
-packages in its site directories. Let's create one, using the location
-we just specified in the environment variable.
+``venv`` is a tool to create isolated Python 3 environments, each with its own
+Python binary and independent set of installed Python packages in its site
+directories. Let's create one, using the location we just specified in the
+environment variable.
.. code-block:: bash
- # Mac and Linux
- $ pyvenv $VENV
+ # macOS and Linux
+ python3 -m venv $VENV
- # Windows
- c:\> c:\Python33\python -m venv %VENV%
+.. code-block:: doscon
-.. seealso:: See also Python 3's :mod:`venv module <python3:venv>`,
- Python 2's `virtualenv <http://www.virtualenv.org/en/latest/>`_
- package,
- :ref:`Installing Pyramid on a Windows System <installing_windows>`
+ # Windows
+ python -m venv %VENV%
+.. seealso:: See also Python 3's :mod:`venv module <python:venv>` and Python
+ 2's `virtualenv <https://virtualenv.pypa.io/en/latest/>`_ package.
-.. _install-setuptools-(python-packaging-tools):
-Install ``setuptools`` (Python packaging tools)
------------------------------------------------
+Update packaging tools in the virtual environment
+-------------------------------------------------
-The following command will download a script to install ``setuptools``, then
-pipe it to your environment's version of Python.
+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
- $ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | $VENV/bin/python
-
- # Windows
- #
- # Use your web browser to download this file:
- # https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
- #
- # ...and save it to:
- # c:\projects\quick_tutorial\ez_setup.py
- #
- # Then run the following command:
-
- c:\> %VENV%\Scripts\python ez_setup.py
+ # macOS and Linux
+ $VENV/bin/pip install --upgrade pip setuptools
-If ``wget`` complains with a certificate error, then run this command instead:
+.. code-block:: doscon
-.. code-block:: bash
+ # Windows
+ %VENV%\Scripts\pip install --upgrade pip setuptools
- # Mac and Linux
- $ wget --no-check-certificate https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | $VENV/bin/python
+.. seealso:: See also :ref:`Why use $VENV/bin/pip instead of source
+ bin/activate, then pip <venv-bin-pip-vs-source-bin-activate>`.
.. _install-pyramid:
@@ -212,48 +189,16 @@ Install Pyramid
---------------
We have our Python standard prerequisites out of the way. The Pyramid
-part is pretty easy:
+part is pretty easy. We'll also install a WSGI server, Waitress.
.. parsed-literal::
- # Mac and Linux
- $ $VENV/bin/easy_install "pyramid==\ |release|\ "
+ # macOS and Linux
+ $VENV/bin/pip install "pyramid==\ |release|\ " waitress
# Windows
- c:\\> %VENV%\\Scripts\\easy_install "pyramid==\ |release|\ "
-
-Our Python virtual environment now has the Pyramid software available.
-
-You can optionally install some of the extra Python packages used
-during this tutorial:
-
-.. code-block:: bash
-
- # Mac and Linux
- $ $VENV/bin/easy_install nose webtest deform sqlalchemy \
- pyramid_chameleon pyramid_debugtoolbar waitress \
- pyramid_tm zope.sqlalchemy
-
- # Windows
- c:\> %VENV%\Scripts\easy_install nose webtest deform sqlalchemy pyramid_chameleon pyramid_debugtoolbar waitress pyramid_tm zope.sqlalchemy
-
-
-.. note::
+ %VENV%\\Scripts\\pip install "pyramid==\ |release|\ " waitress
- Why ``easy_install`` and not ``pip``? Pyramid encourages use of namespace
- packages, for which ``pip``'s support is less-than-optimal. Also, Pyramid's
- dependencies use some optional C extensions for performance: with
- ``easy_install``, Windows users can get these extensions without needing
- a C compiler (``pip`` does not support installing binary Windows
- distributions, except for ``wheels``, which are not yet available for
- all dependencies).
-
-.. seealso:: See also :ref:`installing_unix`. For instructions to set up your
- Python environment for development using Windows or Python 2, see Pyramid's
- :ref:`Before You Install <installing_chapter>`.
-
- See also Python 3's :mod:`venv module <python3:venv>`, the `setuptools
- installation instructions
- <https://pypi.python.org/pypi/setuptools/0.9.8#installation-instructions>`_,
- and `easy_install help <https://pypi.python.org/pypi/setuptools/0.9.8#using-setuptools-and-easyinstall>`_.
+Our Python virtual environment now has the Pyramid software available
+as well as the ``waitress`` package.
diff --git a/docs/quick_tutorial/retail_forms/development.ini b/docs/quick_tutorial/retail_forms/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/retail_forms/development.ini
+++ b/docs/quick_tutorial/retail_forms/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/retail_forms/setup.py b/docs/quick_tutorial/retail_forms/setup.py
index 361ade013..dda0a2cc4 100644
--- a/docs/quick_tutorial/retail_forms/setup.py
+++ b/docs/quick_tutorial/retail_forms/setup.py
@@ -1,15 +1,20 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
+ 'deform',
'pyramid',
'pyramid_chameleon',
- 'deform'
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+setup(
+ name='tutorial',
+ install_requires=requires,
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/routing.rst b/docs/quick_tutorial/routing.rst
index 54dff5c39..a6538a75f 100644
--- a/docs/quick_tutorial/routing.rst
+++ b/docs/quick_tutorial/routing.rst
@@ -4,41 +4,44 @@
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
+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::
- Why do this twice? Other Python web frameworks let you create a
- route and associate it with a view in one step. As
- illustrated in :ref:`routes_need_ordering`, multiple routes might match the
- same URL pattern. Rather than provide ways to help guess, Pyramid lets you
- be explicit in ordering. Pyramid also gives facilities to avoid the
- problem. It's relatively easy to build a system that uses implicit route
- ordering with Pyramid too. See `The Groundhog series of screencasts
- <http://bfg.repoze.org/videos#groundhog1>`_ if you're interested in
+ Why do this twice? Other Python web frameworks let you create a route and
+ associate it with a view in one step. As illustrated in
+ :ref:`routes_need_ordering`, multiple routes might match the same URL
+ pattern. Rather than provide ways to help guess, Pyramid lets you be
+ explicit in ordering. Pyramid also gives facilities to avoid the problem.
+ It's relatively easy to build a system that uses implicit route ordering
+ with Pyramid too. See `The Groundhog series of screencasts
+ <http://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
=====
@@ -47,45 +50,48 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes routing; cd routing
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r view_classes routing; cd routing
+ $VENV/bin/pip install -e .
#. Our ``routing/tutorial/__init__.py`` needs a route with a replacement
pattern:
.. literalinclude:: routing/tutorial/__init__.py
- :linenos:
+ :linenos:
#. We just need one view in ``routing/tutorial/views.py``:
.. literalinclude:: routing/tutorial/views.py
- :linenos:
+ :linenos:
#. We just need one view in ``routing/tutorial/home.pt``:
.. literalinclude:: routing/tutorial/home.pt
- :language: html
- :linenos:
+ :language: html
+ :linenos:
#. Update ``routing/tutorial/tests.py``:
.. literalinclude:: routing/tutorial/tests.py
- :linenos:
+ :linenos:
#. Now run the tests:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
+ $VENV/bin/pytest tutorial/tests.py -q
+ ..
+ 2 passed in 0.39 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/howdy/amy/smith in your browser.
+
Analysis
========
@@ -95,27 +101,24 @@ In ``__init__.py`` we see an important change in our route declaration:
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
+ <https://web.archive.org/web/20170131192830/http://www.plope.com/weird_pyramid_urldispatch>`_
diff --git a/docs/quick_tutorial/routing/development.ini b/docs/quick_tutorial/routing/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/routing/development.ini
+++ b/docs/quick_tutorial/routing/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/routing/setup.py b/docs/quick_tutorial/routing/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/routing/setup.py
+++ b/docs/quick_tutorial/routing/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/routing/tutorial/home.pt b/docs/quick_tutorial/routing/tutorial/home.pt
index f2b991059..b68e96338 100644
--- a/docs/quick_tutorial/routing/tutorial/home.pt
+++ b/docs/quick_tutorial/routing/tutorial/home.pt
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>${name}</h1>
<p>First: ${first}, Last: ${last}</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/scaffolds.rst b/docs/quick_tutorial/scaffolds.rst
deleted file mode 100644
index 4f2694100..000000000
--- a/docs/quick_tutorial/scaffolds.rst
+++ /dev/null
@@ -1,86 +0,0 @@
-.. _qtut_scaffolds:
-
-=============================================
-Prelude: Quick Project Startup with Scaffolds
-=============================================
-
-To ease the process of getting started, Pyramid provides *scaffolds*
-that generate sample projects from templates in Pyramid and Pyramid
-add-ons.
-
-Background
-==========
-
-We're going to cover a lot in this tutorial, focusing on one topic at a
-time and writing everything from scratch. As a warmup, though,
-it sure would be nice to see some pixels on a screen.
-
-Like other web development frameworks, Pyramid provides a number of
-"scaffolds" that generate working Python, template, and CSS code for
-sample applications. In this step we'll use a built-in scaffold to let
-us preview a Pyramid application, before starting from scratch on Step 1.
-
-Objectives
-==========
-
-- Use Pyramid's ``pcreate`` command to list scaffolds and make a new
- project
-
-- Start up a Pyramid application and visit it in a web browser
-
-Steps
-=====
-
-#. Pyramid's ``pcreate`` command can list the available scaffolds:
-
- .. code-block:: bash
-
- $ $VENV/bin/pcreate --list
- Available scaffolds:
- alchemy: Pyramid SQLAlchemy project using url dispatch
- starter: Pyramid starter project
- zodb: Pyramid ZODB project using traversal
-
-#. Tell ``pcreate`` to use the ``starter`` scaffold to make our project:
-
- .. code-block:: bash
-
- $ $VENV/bin/pcreate --scaffold starter scaffolds
-
-#. Use normal Python development to setup our project for development:
-
- .. code-block:: bash
-
- $ cd scaffolds
- $ $VENV/bin/python setup.py develop
-
-#. Startup the application by pointing Pyramid's ``pserve`` command at
- the project's (generated) configuration file:
-
- .. code-block:: bash
-
- $ $VENV/bin/pserve development.ini --reload
-
- On startup, ``pserve`` logs some output:
-
- .. code-block:: bash
-
- Starting subprocess with file monitor
- Starting server in PID 72213.
- Starting HTTP server on http://0.0.0.0:6543
-
-#. Open http://localhost:6543/ in your browser.
-
-Analysis
-========
-
-Rather than starting from scratch, ``pcreate`` can make getting a
-Python project containing a Pyramid application a quick matter.
-Pyramid ships with a few scaffolds. But installing a Pyramid add-on can
-give you new scaffolds from that add-on.
-
-``pserve`` is Pyramid's application runner, separating operational
-details from your code. When you install Pyramid, a small command
-program called ``pserve`` is written to your ``bin`` directory. This
-program is an executable Python module. It is passed a configuration
-file (in this case, ``development.ini``.)
diff --git a/docs/quick_tutorial/scaffolds/CHANGES.txt b/docs/quick_tutorial/scaffolds/CHANGES.txt
deleted file mode 100644
index 35a34f332..000000000
--- a/docs/quick_tutorial/scaffolds/CHANGES.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-0.0
----
-
-- Initial version
diff --git a/docs/quick_tutorial/scaffolds/MANIFEST.in b/docs/quick_tutorial/scaffolds/MANIFEST.in
deleted file mode 100644
index 91d3f763b..000000000
--- a/docs/quick_tutorial/scaffolds/MANIFEST.in
+++ /dev/null
@@ -1,2 +0,0 @@
-include *.txt *.ini *.cfg *.rst
-recursive-include scaffolds *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/quick_tutorial/scaffolds/README.txt b/docs/quick_tutorial/scaffolds/README.txt
deleted file mode 100644
index 7776dd2d5..000000000
--- a/docs/quick_tutorial/scaffolds/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-scaffolds README
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/__init__.py b/docs/quick_tutorial/scaffolds/scaffolds/__init__.py
deleted file mode 100644
index ad5ecbc6f..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from pyramid.config import Configurator
-
-
-def main(global_config, **settings):
- """ This function returns a Pyramid WSGI application.
- """
- config = Configurator(settings=settings)
- config.include('pyramid_chameleon')
- config.add_static_view('static', 'static', cache_max_age=3600)
- config.add_route('home', '/')
- config.scan()
- return config.make_wsgi_app()
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/favicon.ico b/docs/quick_tutorial/scaffolds/scaffolds/static/favicon.ico
deleted file mode 100644
index 71f837c9e..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.png b/docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.png
deleted file mode 100644
index 1fbc873da..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.png b/docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.png
deleted file mode 100644
index 0596f2020..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.png
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css b/docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css
deleted file mode 100644
index b7c8493d8..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css
+++ /dev/null
@@ -1,8 +0,0 @@
-* html img,
-* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
-this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
-this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
-this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
-this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
-);}
-#wrap{display:table;height:100%}
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.png b/docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.png
deleted file mode 100644
index 2369cfb7d..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.png
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css b/docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css
deleted file mode 100644
index 4b1c017cd..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css
+++ /dev/null
@@ -1,372 +0,0 @@
-html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
-{
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- font-size: 100%; /* 16px */
- vertical-align: baseline;
- background: transparent;
-}
-
-body
-{
- line-height: 1;
-}
-
-ol, ul
-{
- list-style: none;
-}
-
-blockquote, q
-{
- quotes: none;
-}
-
-blockquote:before, blockquote:after, q:before, q:after
-{
- content: '';
- content: none;
-}
-
-:focus
-{
- outline: 0;
-}
-
-ins
-{
- text-decoration: none;
-}
-
-del
-{
- text-decoration: line-through;
-}
-
-table
-{
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-sub
-{
- vertical-align: sub;
- font-size: smaller;
- line-height: normal;
-}
-
-sup
-{
- vertical-align: super;
- font-size: smaller;
- line-height: normal;
-}
-
-ul, menu, dir
-{
- display: block;
- list-style-type: disc;
- margin: 1em 0;
- padding-left: 40px;
-}
-
-ol
-{
- display: block;
- list-style-type: decimal-leading-zero;
- margin: 1em 0;
- padding-left: 40px;
-}
-
-li
-{
- display: list-item;
-}
-
-ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
-{
- margin-top: 0;
- margin-bottom: 0;
-}
-
-ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
-{
- list-style-type: circle;
-}
-
-ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
-{
- list-style-type: square;
-}
-
-.hidden
-{
- display: none;
-}
-
-p
-{
- line-height: 1.5em;
-}
-
-h1
-{
- font-size: 1.75em;
- line-height: 1.7em;
- font-family: helvetica, verdana;
-}
-
-h2
-{
- font-size: 1.5em;
- line-height: 1.7em;
- font-family: helvetica, verdana;
-}
-
-h3
-{
- font-size: 1.25em;
- line-height: 1.7em;
- font-family: helvetica, verdana;
-}
-
-h4
-{
- font-size: 1em;
- line-height: 1.7em;
- font-family: helvetica, verdana;
-}
-
-html, body
-{
- width: 100%;
- height: 100%;
-}
-
-body
-{
- margin: 0;
- padding: 0;
- background-color: #fff;
- position: relative;
- font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
-}
-
-a
-{
- color: #1b61d6;
- text-decoration: none;
-}
-
-a:hover
-{
- color: #e88f00;
- text-decoration: underline;
-}
-
-body h1, body h2, body h3, body h4, body h5, body h6
-{
- font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
- font-weight: 400;
- color: #373839;
- font-style: normal;
-}
-
-#wrap
-{
- min-height: 100%;
-}
-
-#header, #footer
-{
- width: 100%;
- color: #fff;
- height: 40px;
- position: absolute;
- text-align: center;
- line-height: 40px;
- overflow: hidden;
- font-size: 12px;
- vertical-align: middle;
-}
-
-#header
-{
- background: #000;
- top: 0;
- font-size: 14px;
-}
-
-#footer
-{
- bottom: 0;
- background: #000 url(footerbg.png) repeat-x 0 top;
- position: relative;
- margin-top: -40px;
- clear: both;
-}
-
-.header, .footer
-{
- width: 750px;
- margin-right: auto;
- margin-left: auto;
-}
-
-.wrapper
-{
- width: 100%;
-}
-
-#top, #top-small, #bottom
-{
- width: 100%;
-}
-
-#top
-{
- color: #000;
- height: 230px;
- background: #fff url(headerbg.png) repeat-x 0 top;
- position: relative;
-}
-
-#top-small
-{
- color: #000;
- height: 60px;
- background: #fff url(headerbg.png) repeat-x 0 top;
- position: relative;
-}
-
-#bottom
-{
- color: #222;
- background-color: #fff;
-}
-
-.top, .top-small, .middle, .bottom
-{
- width: 750px;
- margin-right: auto;
- margin-left: auto;
-}
-
-.top
-{
- padding-top: 40px;
-}
-
-.top-small
-{
- padding-top: 10px;
-}
-
-#middle
-{
- width: 100%;
- height: 100px;
- background: url(middlebg.png) repeat-x;
- border-top: 2px solid #fff;
- border-bottom: 2px solid #b2b2b2;
-}
-
-.app-welcome
-{
- margin-top: 25px;
-}
-
-.app-name
-{
- color: #000;
- font-weight: 700;
-}
-
-.bottom
-{
- padding-top: 50px;
-}
-
-#left
-{
- width: 350px;
- float: left;
- padding-right: 25px;
-}
-
-#right
-{
- width: 350px;
- float: right;
- padding-left: 25px;
-}
-
-.align-left
-{
- text-align: left;
-}
-
-.align-right
-{
- text-align: right;
-}
-
-.align-center
-{
- text-align: center;
-}
-
-ul.links
-{
- margin: 0;
- padding: 0;
-}
-
-ul.links li
-{
- list-style-type: none;
- font-size: 14px;
-}
-
-form
-{
- border-style: none;
-}
-
-fieldset
-{
- border-style: none;
-}
-
-input
-{
- color: #222;
- border: 1px solid #ccc;
- font-family: sans-serif;
- font-size: 12px;
- line-height: 16px;
-}
-
-input[type=text], input[type=password]
-{
- width: 205px;
-}
-
-input[type=submit]
-{
- background-color: #ddd;
- font-weight: 700;
-}
-
-/*Opera Fix*/
-body:before
-{
- content: "";
- height: 100%;
- float: left;
- width: 0;
- margin-top: -32767px;
-}
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.png b/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.png
deleted file mode 100644
index a5bc0ade7..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.png
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.png b/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.png
deleted file mode 100644
index 347e05549..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.png
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gif b/docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gif
deleted file mode 100644
index 0341802e5..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gif
+++ /dev/null
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt b/docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt
deleted file mode 100644
index b43a174e3..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt
+++ /dev/null
@@ -1,73 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
-<head>
- <title>The Pyramid Web Framework</title>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
- <meta name="keywords" content="python web application" />
- <meta name="description" content="pyramid web application" />
- <link rel="shortcut icon" href="${request.static_url('scaffolds:static/favicon.ico')}" />
- <link rel="stylesheet" href="${request.static_url('scaffolds:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
- <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
- <!--[if lte IE 6]>
- <link rel="stylesheet" href="${request.static_url('scaffolds:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
- <![endif]-->
-</head>
-<body>
- <div id="wrap">
- <div id="top">
- <div class="top align-center">
- <div><img src="${request.static_url('scaffolds:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
- </div>
- </div>
- <div id="middle">
- <div class="middle align-center">
- <p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid Web Framework.
- </p>
- </div>
- </div>
- <div id="bottom">
- <div class="bottom">
- <div id="left" class="align-right">
- <h2>Search documentation</h2>
- <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
- </div>
- <div id="right" class="align-left">
- <h2>Pyramid links</h2>
- <ul class="links">
- <li>
- <a href="http://pylonsproject.org">Pylons Website</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a>
- </li>
- <li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a>
- </li>
- <li>
- <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
-</body>
-</html>
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/tests.py b/docs/quick_tutorial/scaffolds/scaffolds/tests.py
deleted file mode 100644
index 4f906ffa9..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/tests.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import unittest
-
-from pyramid import testing
-
-
-class ViewTests(unittest.TestCase):
- def setUp(self):
- self.config = testing.setUp()
-
- def tearDown(self):
- testing.tearDown()
-
- def test_my_view(self):
- from .views import my_view
- request = testing.DummyRequest()
- info = my_view(request)
- self.assertEqual(info['project'], 'scaffolds')
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/views.py b/docs/quick_tutorial/scaffolds/scaffolds/views.py
deleted file mode 100644
index db90d8364..000000000
--- a/docs/quick_tutorial/scaffolds/scaffolds/views.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from pyramid.view import view_config
-
-
-@view_config(route_name='home', renderer='templates/mytemplate.pt')
-def my_view(request):
- return {'project': 'scaffolds'}
diff --git a/docs/quick_tutorial/scaffolds/setup.py b/docs/quick_tutorial/scaffolds/setup.py
deleted file mode 100644
index ec95946a5..000000000
--- a/docs/quick_tutorial/scaffolds/setup.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import os
-
-from setuptools import setup, find_packages
-
-here = os.path.abspath(os.path.dirname(__file__))
-with open(os.path.join(here, 'README.txt')) as f:
- README = f.read()
-with open(os.path.join(here, 'CHANGES.txt')) as f:
- CHANGES = f.read()
-
-requires = [
- 'pyramid',
- 'pyramid_chameleon',
- 'pyramid_debugtoolbar',
- 'waitress',
- ]
-
-setup(name='scaffolds',
- version='0.0',
- description='scaffolds',
- long_description=README + '\n\n' + CHANGES,
- classifiers=[
- "Programming Language :: Python",
- "Framework :: Pyramid",
- "Topic :: Internet :: WWW/HTTP",
- "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
- ],
- author='',
- author_email='',
- url='',
- keywords='web pyramid pylons',
- packages=find_packages(),
- include_package_data=True,
- zip_safe=False,
- install_requires=requires,
- tests_require=requires,
- test_suite="scaffolds",
- entry_points="""\
- [paste.app_factory]
- main = scaffolds:main
- """,
- )
diff --git a/docs/quick_tutorial/sessions.rst b/docs/quick_tutorial/sessions.rst
index 0f284e9a7..8a67d6a0f 100644
--- a/docs/quick_tutorial/sessions.rst
+++ b/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
+<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>`.
-Pyramid has basic built-in support for sessions, with add-ons
-or your own custom sessioning engine) that can provide
-richer session support. Let's take a look at the
-:ref:`built-in sessioning support <sessions_chapter>`.
Objectives
==========
-- Make a session factory using a built-in, simple Pyramid sessioning
- system
+- 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
=====
@@ -33,66 +36,67 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes sessions; cd sessions
- $ $VENV/bin/python setup.py develop
+ 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:
+ :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:
+ :linenos:
#. The template at ``sessions/tutorial/home.pt`` can display the value:
.. literalinclude:: sessions/tutorial/home.pt
- :language: html
- :linenos:
+ :language: html
+ :linenos:
#. Make sure the tests still pass:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 4 passed in 0.42 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $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 requests.
+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`,
diff --git a/docs/quick_tutorial/sessions/development.ini b/docs/quick_tutorial/sessions/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/sessions/development.ini
+++ b/docs/quick_tutorial/sessions/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/sessions/setup.py b/docs/quick_tutorial/sessions/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/sessions/setup.py
+++ b/docs/quick_tutorial/sessions/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/sessions/tutorial/home.pt b/docs/quick_tutorial/sessions/tutorial/home.pt
index 0b27ba1d8..50342e52f 100644
--- a/docs/quick_tutorial/sessions/tutorial/home.pt
+++ b/docs/quick_tutorial/sessions/tutorial/home.pt
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
<p>Count: ${view.counter}</p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/static_assets.rst b/docs/quick_tutorial/static_assets.rst
index 19d33f00f..567328307 100644
--- a/docs/quick_tutorial/static_assets.rst
+++ b/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
=====
@@ -22,65 +23,74 @@ Steps
.. code-block:: bash
- $ cd ..; cp -r view_classes static_assets; cd static_assets
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r view_classes static_assets; cd static_assets
+ $VENV/bin/pip install -e .
-#. We add a call ``config.add_static_view in
+#. We add a call ``config.add_static_view`` in
``static_assets/tutorial/__init__.py``:
.. literalinclude:: static_assets/tutorial/__init__.py
- :linenos:
+ :linenos:
#. We can add a CSS link in the ``<head>`` of our template at
``static_assets/tutorial/home.pt``:
.. literalinclude:: static_assets/tutorial/home.pt
- :language: html
+ :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
+ :language: css
+
+#. We add a functional test that asserts that the newly added static file is delivered:
+
+ .. literalinclude:: static_assets/tutorial/tests.py
+ :language: python
+ :pyobject: TutorialFunctionalTests.test_css
+ :lineno-match:
-#. Make sure we haven't broken any existing code by running the tests:
+#. Now run the tests:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 5 passed in 0.50 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ in your browser and note the new font.
+
Analysis
========
We changed our WSGI application to map requests under
-http://localhost:6543/static/ to files and directories inside a
-``static`` directory inside our ``tutorial`` package. This directory
-contained ``app.css``.
+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')}
+ ${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
diff --git a/docs/quick_tutorial/static_assets/development.ini b/docs/quick_tutorial/static_assets/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/static_assets/development.ini
+++ b/docs/quick_tutorial/static_assets/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/static_assets/setup.py b/docs/quick_tutorial/static_assets/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/static_assets/setup.py
+++ b/docs/quick_tutorial/static_assets/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/static_assets/tutorial/home.pt b/docs/quick_tutorial/static_assets/tutorial/home.pt
index 5d347f057..57867a1ff 100644
--- a/docs/quick_tutorial/static_assets/tutorial/home.pt
+++ b/docs/quick_tutorial/static_assets/tutorial/home.pt
@@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
<link rel="stylesheet"
href="${request.static_url('tutorial:static/app.css') }"/>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/static_assets/tutorial/tests.py b/docs/quick_tutorial/static_assets/tutorial/tests.py
index 4381235ec..b560ddf82 100644
--- a/docs/quick_tutorial/static_assets/tutorial/tests.py
+++ b/docs/quick_tutorial/static_assets/tutorial/tests.py
@@ -42,3 +42,7 @@ class TutorialFunctionalTests(unittest.TestCase):
def test_hello(self):
res = self.testapp.get('/howdy', status=200)
self.assertIn(b'<h1>Hi Hello View', res.body)
+
+ def test_css(self):
+ res = self.testapp.get('/static/app.css', status=200)
+ self.assertIn(b'body', res.body)
diff --git a/docs/quick_tutorial/templating.rst b/docs/quick_tutorial/templating.rst
index d73067f48..cef54fb6f 100644
--- a/docs/quick_tutorial/templating.rst
+++ b/docs/quick_tutorial/templating.rst
@@ -4,120 +4,114 @@
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, 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!
-Pyramid doesn't mandate a particular database system, form library,
-etc. It encourages replaceability. This applies equally to templating,
-which is fortunate: developers have strong views about template
-languages. As of Pyramid 1.5a2, Pyramid doesn't even bundle a template
-language!
+It does, however, have strong ties to Jinja2, Mako, and Chameleon. In this step
+we see how to add `pyramid_chameleon
+<https://github.com/Pylons/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`` 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:
-
- .. code-block:: bash
+#. Let's begin by using the previous package as a starting point for a new project:
- $ cd ..; cp -r views templating; cd templating
+ .. code-block:: bash
-#. This step depends on ``pyramid_chameleon``, so add it as a dependency
- in ``templating/setup.py``:
+ cd ..; cp -r views templating; cd templating
- .. literalinclude:: templating/setup.py
- :linenos:
+#. This step depends on ``pyramid_chameleon``, so add it as a dependency in ``templating/setup.py``:
-#. Now we can activate the development-mode distribution:
+ .. literalinclude:: templating/setup.py
+ :linenos:
+ :emphasize-lines: 7
- .. code-block:: bash
+#. Now we can activate the development-mode distribution:
- $ $VENV/bin/python setup.py develop
+ .. code-block:: bash
-#. We need to connect ``pyramid_chameleon`` as a renderer by making a
- call in the setup of ``templating/tutorial/__init__.py``:
+ $VENV/bin/pip install -e .
- .. literalinclude:: templating/tutorial/__init__.py
- :linenos:
+#. We need to connect ``pyramid_chameleon`` as a renderer by making a call in the setup of ``templating/tutorial/__init__.py``:
-#. Our ``templating/tutorial/views.py`` no longer has HTML in it:
+ .. literalinclude:: templating/tutorial/__init__.py
+ :linenos:
- .. literalinclude:: templating/tutorial/views.py
- :linenos:
+#. Our ``templating/tutorial/views.py`` no longer has HTML in it:
-#. Instead we have ``templating/tutorial/home.pt`` as a template:
+ .. literalinclude:: templating/tutorial/views.py
+ :linenos:
- .. literalinclude:: templating/tutorial/home.pt
- :language: html
+#. Instead we have ``templating/tutorial/home.pt`` as a template:
-#. For convenience, change ``templating/development.ini`` to reload
- templates automatically with ``pyramid.reload_templates``:
+ .. literalinclude:: templating/tutorial/home.pt
+ :language: html
- .. literalinclude:: templating/development.ini
- :language: ini
+#. For convenience, change ``templating/development.ini`` to reload templates automatically with ``pyramid.reload_templates``:
-#. Our unit tests in ``templating/tutorial/tests.py`` can focus on
- data:
+ .. literalinclude:: templating/development.ini
+ :language: ini
- .. literalinclude:: templating/tutorial/tests.py
- :linenos:
+#. Our unit tests in ``templating/tutorial/tests.py`` can focus on data:
-#. Now run the tests:
+ .. literalinclude:: templating/tutorial/tests.py
+ :linenos:
- .. code-block:: bash
+#. Now run the tests:
+ .. code-block:: bash
- $ $VENV/bin/nosetests tutorial
- .
- ----------------------------------------------------------------------
- Ran 4 tests in 0.141s
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 4 passed in 0.46 seconds
- OK
+#. Run your Pyramid application with:
-#. Run your Pyramid application with:
+ .. code-block:: bash
- .. code-block:: bash
+ $VENV/bin/pserve development.ini --reload
- $ $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
-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`.
diff --git a/docs/quick_tutorial/templating/development.ini b/docs/quick_tutorial/templating/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/templating/development.ini
+++ b/docs/quick_tutorial/templating/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/templating/setup.py b/docs/quick_tutorial/templating/setup.py
index 0b71b73e6..e9c068a23 100644
--- a/docs/quick_tutorial/templating/setup.py
+++ b/docs/quick_tutorial/templating/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
)
diff --git a/docs/quick_tutorial/templating/tutorial/home.pt b/docs/quick_tutorial/templating/tutorial/home.pt
index a0cc08e7a..fd4ef8764 100644
--- a/docs/quick_tutorial/templating/tutorial/home.pt
+++ b/docs/quick_tutorial/templating/tutorial/home.pt
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
- <title>Quick Tour: ${name}</title>
+ <title>Quick Tutorial: ${name}</title>
</head>
<body>
<h1>Hi ${name}</h1>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/quick_tutorial/tutorial_approach.rst b/docs/quick_tutorial/tutorial_approach.rst
index 204d388b0..a16bce406 100644
--- a/docs/quick_tutorial/tutorial_approach.rst
+++ b/docs/quick_tutorial/tutorial_approach.rst
@@ -2,44 +2,47 @@
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.
-To successfully run each step::
+Directory tree
+==============
- $ cd request_response
- $ $VENV/bin/python setup.py develop
+This "Getting Started" tutorial is broken into independent steps, starting with
+the smallest possible "single file WSGI app" example. Each of these steps
+introduces a topic and a very small set of concepts via working code. The steps
+each correspond to a directory in our workspace, where each step's directory is
+a Python package. Source code used in this tutorial is located in the `Pyramid repository in the directory "docs/quick_tutorial" <https://github.com/Pylons/pyramid/>`_. You may ``git clone`` the repository, download, or copy-paste the source code. If you do so, then make sure you use the same branch as this documentation.
-...and repeat for each step you would like to work on. In most cases we
-will start with the results of an earlier step.
+As we develop our tutorial, our directory tree will resemble the structure
+below:
-Directory Tree
-==============
+.. code-block:: text
+
+ quick_tutorial
+ ├── env
+ └── request_response
+ ├── tutorial
+ │ ├── __init__.py
+ │ ├── tests.py
+ │ └── views.py
+ ├── development.ini
+ └── setup.py
+
+Each of the directories in our ``quick_tutorial`` workspace (e.g., ``request_response``) is a *Python
+project* (except as noted for the ``hello_world`` step). The ``tutorial``
+directory is a *Python package*.
+
+For most steps you will copy an earlier step's directory to a new directory, change your working directory to the new directory, then install your project:
+
+.. code-block:: bash
+
+ cd ..; cp -r package ini; cd ini
+ $VENV/bin/pip install -e .
+
+For a few steps, you won't copy an earlier step's directory, but you will still need to install your project with ``$VENV/bin/pip install -e .``.
-As we develop our tutorial our directory tree will resemble the
-structure below::
-
- quicktutorial/
- request_response/
- development.ini
- setup.py
- tutorial/
- __init__.py
- home.pt
- tests.py
- views.py
-
-Each of the first-level directories (e.g. ``request_response``) is a
-*Python project* (except, as noted, the ``hello_world`` step.) The
-``tutorial`` directory is a *Python package*. At the end of each step,
-we copy a previous directory into a new directory to use as a starting
-point.
+Finally for a few steps, you might add a dependency to your project in its ``setup.py`` file, and then install both the dependency and the project with either ``$VENV/bin/pip install -e .`` or ``$VENV/bin/pip install -e ".[dev]"``.
diff --git a/docs/quick_tutorial/unit_testing.rst b/docs/quick_tutorial/unit_testing.rst
index f8a33b39d..654925347 100644
--- a/docs/quick_tutorial/unit_testing.rst
+++ b/docs/quick_tutorial/unit_testing.rst
@@ -1,114 +1,123 @@
.. _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.
-
-Python includes a
-:ref:`unit testing framework <python:unittest-minimal-example>` in its
-standard library. Over the years a number of Python projects, such as
-`nose <https://pypi.python.org/pypi/nose/>`_, have extended this
-framework with alternative test runners that provide more convenience
-and functionality. The Pyramid developers use ``nose``, which we'll thus
-use in this tutorial.
-
-Don't worry, this tutorial won't be pedantic about "test-driven
-development" (TDD.) We'll do just enough to ensure that, in each step,
-we haven't majorly broken the code. As you're writing your code you
-might find this more convenient than changing to your browser
-constantly and clicking reload.
-
-We'll also leave discussion of
-`coverage <https://pypi.python.org/pypi/coverage>`_ for another section.
+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 :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.
+
+We'll also leave discussion of `pytest-cov
+<https://pytest-cov.readthedocs.io/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 (``pytest``) which helps in our testing.
-- Install a Python package (``nose``) which helps in our testing
Steps
=====
-#. First we copy the results of the previous step, as well as install
- the ``nose`` package:
+#. First we copy the results of the previous step.
- .. code-block:: bash
+ .. code-block:: bash
- $ cd ..; cp -r debugtoolbar unit_testing; cd unit_testing
- $ $VENV/bin/python setup.py develop
- $ $VENV/bin/easy_install nose
+ cd ..; cp -r debugtoolbar unit_testing; cd unit_testing
-#. Now we write a simple unit test in ``unit_testing/tutorial/tests.py``:
+#. Add ``pytest`` to our project's dependencies in ``setup.py`` as a :term:`Setuptools` "extra":
- .. literalinclude:: unit_testing/tutorial/tests.py
- :linenos:
+ .. literalinclude:: unit_testing/setup.py
+ :language: python
+ :linenos:
+ :emphasize-lines: 15
-#. Now run the tests:
+#. Install our project and its newly added dependency.
+ Note that we use the extra specifier ``[dev]`` to install testing requirements for development and surround it and the period with double quote marks.
- .. code-block:: bash
+ .. code-block:: bash
+ $VENV/bin/pip install -e ".[dev]"
- $ $VENV/bin/nosetests tutorial
- .
- ----------------------------------------------------------------------
- Ran 1 test in 0.141s
+#. Now we write a simple unit test in ``unit_testing/tutorial/tests.py``:
+
+ .. literalinclude:: unit_testing/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+
+ $VENV/bin/pytest tutorial/tests.py -q
+ .
+ 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
+
+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 ``pytest`` 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?
@@ -116,4 +125,4 @@ Extra Credit
#. Why do we import the ``hello_world`` view function *inside* the
``test_hello_world`` method instead of at the top of the module?
-.. seealso:: See also :ref:`testing_chapter`
+.. seealso:: See also :ref:`testing_chapter` and `Setuptools Declaring "Extras" (optional features with their own dependencies) <https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies>`_.
diff --git a/docs/quick_tutorial/unit_testing/development.ini b/docs/quick_tutorial/unit_testing/development.ini
index 470d92c57..58d23cff7 100644
--- a/docs/quick_tutorial/unit_testing/development.ini
+++ b/docs/quick_tutorial/unit_testing/development.ini
@@ -4,37 +4,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/unit_testing/setup.py b/docs/quick_tutorial/unit_testing/setup.py
index 9997984d3..a5f8a250b 100644
--- a/docs/quick_tutorial/unit_testing/setup.py
+++ b/docs/quick_tutorial/unit_testing/setup.py
@@ -1,13 +1,29 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/view_classes.rst b/docs/quick_tutorial/view_classes.rst
index 58ab43e40..1307857b7 100644
--- a/docs/quick_tutorial/view_classes.rst
+++ b/docs/quick_tutorial/view_classes.rst
@@ -4,95 +4,87 @@
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
==========
-So far our views have been simple, free-standing functions. Many times
-your views are related: different ways to look at or work on the same
-data or a REST API that handles multiple operations. Grouping these
-together as a
-:ref:`view class <class_as_view>` makes sense:
+So far our views have been simple, free-standing functions. Many times your
+views are related to one another. They may consist of different ways to look at or work
+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:
-#. First we copy the results of the previous step:
-
- .. code-block:: bash
+ .. code-block:: bash
- $ cd ..; cp -r templating view_classes; cd view_classes
- $ $VENV/bin/python setup.py develop
+ 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:
+ .. literalinclude:: view_classes/tutorial/views.py
+ :linenos:
-#. Our unit tests in ``view_classes/tutorial/tests.py`` don't run,
- so let's modify the to import the view class and make an instance
- before getting a response:
+#. 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:
+ .. literalinclude:: view_classes/tutorial/tests.py
+ :linenos:
-#. Now run the tests:
+#. Now run the tests:
- .. code-block:: bash
+ .. code-block:: bash
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 4 passed in 0.34 seconds
- $ $VENV/bin/nosetests tutorial
- .
- ----------------------------------------------------------------------
- Ran 4 tests in 0.141s
+#. Run your Pyramid application with:
- OK
+ .. code-block:: bash
-#. Run your Pyramid application with:
+ $VENV/bin/pserve development.ini --reload
- .. code-block:: bash
+#. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser.
- $ $VENV/bin/pserve development.ini --reload
-
-#. Open http://localhost:6543/ and http://localhost:6543/howdy
- in your browser.
Analysis
========
To ease the transition to view classes, we didn't introduce any new
-functionality. We simply changed the view functions to methods on a
-view class, then updated the tests.
-
-In our ``TutorialViews`` view class you can see that our two view
-classes are logically grouped together as methods on a common class.
-Since the two views shared the same template, we could move that to a
-``@view_defaults`` decorator on at the class level.
-
-The tests needed to change. Obviously we needed to import the view
-class. But you can also see the pattern in the tests of instantiating
-the view class with the dummy request first, then calling the view
-method being tested.
+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.
+
+The tests needed to change. Obviously we needed to import the view class. But
+you can also see the pattern in the tests of instantiating the view class with
+the dummy request first, then calling the view method being tested.
.. seealso:: :ref:`class_as_view`
diff --git a/docs/quick_tutorial/view_classes/development.ini b/docs/quick_tutorial/view_classes/development.ini
index 62e0c5123..78d7479e7 100644
--- a/docs/quick_tutorial/view_classes/development.ini
+++ b/docs/quick_tutorial/view_classes/development.ini
@@ -5,37 +5,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/view_classes/setup.py b/docs/quick_tutorial/view_classes/setup.py
index 2221b72e9..e9c068a23 100644
--- a/docs/quick_tutorial/view_classes/setup.py
+++ b/docs/quick_tutorial/view_classes/setup.py
@@ -1,14 +1,31 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
- 'pyramid_chameleon'
+ 'pyramid_chameleon',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)
diff --git a/docs/quick_tutorial/views.rst b/docs/quick_tutorial/views.rst
index 529bba0a4..45bc8518b 100644
--- a/docs/quick_tutorial/views.rst
+++ b/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,100 +23,99 @@ So far our examples place everything in one file:
- 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.
+
+- Create decorators that do declarative configuration.
-- 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
- $ cd ..; cp -r functional_testing views; cd views
- $ $VENV/bin/python setup.py develop
+ cd ..; cp -r functional_testing views; cd views
+ $VENV/bin/pip install -e .
#. Our ``views/tutorial/__init__.py`` gets a lot shorter:
.. literalinclude:: views/tutorial/__init__.py
- :linenos:
+ :linenos:
#. Let's add a module ``views/tutorial/views.py`` that is focused on
handling requests and responses:
.. literalinclude:: views/tutorial/views.py
- :linenos:
+ :linenos:
#. Update the tests to cover the two new views:
.. literalinclude:: views/tutorial/tests.py
- :linenos:
+ :linenos:
#. Now run the tests:
.. code-block:: bash
- $ $VENV/bin/nosetests tutorial
- .
- ----------------------------------------------------------------------
- Ran 4 tests in 0.141s
-
- OK
+ $VENV/bin/pytest tutorial/tests.py -q
+ ....
+ 4 passed in 0.28 seconds
#. Run your Pyramid application with:
.. code-block:: bash
- $ $VENV/bin/pserve development.ini --reload
+ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/ and http://localhost:6543/howdy
in your browser.
+
Analysis
========
-We added some more URLs, but we also removed the view code from the
-application startup code in ``tutorial/__init__.py``.
-Our views, and their view registrations (via decorators) are now in a
-module ``views.py`` which is scanned via ``config.scan('.views')``.
-
-We have 2 views, each leading to the other. If you start at
-http://localhost:6543/, you get a response with a link to the next
-view. The ``hello_view`` (available at the URL ``/howdy``) has a link
-back to the first view.
-
-This step also shows that the name appearing in the URL,
-the name of the "route" that maps a URL to a view,
-and the name of the view, can all be different. More on routes later.
-
-Earlier we saw ``config.add_view`` as one way to configure a view. This
-section introduces ``@view_config``. Pyramid's configuration supports
-:term:`imperative configuration`, such as the
-``config.add_view`` in the previous example. You can also use
-:term:`declarative configuration`, in which a Python
-:term:`python:decorator`
-is placed on the line above the view. Both approaches result in the
-same final configuration, thus usually, it is simply a matter of taste.
-
-Extra Credit
+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 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.
+
+Earlier we saw ``config.add_view`` as one way to configure a view. This section
+introduces ``@view_config``. Pyramid's configuration supports :term:`imperative
+configuration`, such as the ``config.add_view`` in the previous example. You
+can also use :term:`declarative configuration`, in which a Python
+:term:`python:decorator` is placed on the line above the view. Both approaches
+result in the same final configuration, thus usually, it is simply a matter of
+taste.
+
+
+Extra credit
============
#. What does the dot in ``.views`` signify?
-#. Why might ``assertIn`` be a better choice in testing the text in
- responses than ``assertEqual``?
+#. Why might ``assertIn`` be a better choice in testing the text in responses
+ than ``assertEqual``?
.. seealso:: :ref:`views_chapter`,
:ref:`view_config_chapter`, and
:ref:`debugging_view_configuration`
-
diff --git a/docs/quick_tutorial/views/development.ini b/docs/quick_tutorial/views/development.ini
index 470d92c57..58d23cff7 100644
--- a/docs/quick_tutorial/views/development.ini
+++ b/docs/quick_tutorial/views/development.ini
@@ -4,37 +4,5 @@ pyramid.includes =
pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
-host = 0.0.0.0
-port = 6543
-
-# Begin logging configuration
-
-[loggers]
-keys = root, tutorial
-
-[logger_tutorial]
-level = DEBUG
-handlers =
-qualname = tutorial
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = INFO
-handlers = console
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
+use = egg:waitress#main
+listen = localhost:6543
diff --git a/docs/quick_tutorial/views/setup.py b/docs/quick_tutorial/views/setup.py
index 9997984d3..a0fa8217c 100644
--- a/docs/quick_tutorial/views/setup.py
+++ b/docs/quick_tutorial/views/setup.py
@@ -1,13 +1,30 @@
from setuptools import setup
+# List of dependencies installed via `pip install -e .`
+# by virtue of the Setuptools `install_requires` value below.
requires = [
'pyramid',
+ 'waitress',
]
-setup(name='tutorial',
- install_requires=requires,
- entry_points="""\
- [paste.app_factory]
- main = tutorial:main
- """,
-) \ No newline at end of file
+# List of dependencies installed via `pip install -e ".[dev]"`
+# by virtue of the Setuptools `extras_require` value in the Python
+# dictionary below.
+dev_requires = [
+ 'pyramid_debugtoolbar',
+ 'pytest',
+ 'webtest',
+]
+
+setup(
+ name='tutorial',
+ install_requires=requires,
+ extras_require={
+ 'dev': dev_requires,
+ },
+ entry_points={
+ 'paste.app_factory': [
+ 'main = tutorial:main'
+ ],
+ },
+)