From 407b335ed9954c042377fd2e060c36edcd07cf60 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 23:45:24 -0500 Subject: add support for using an absolute path to override an asset fixes #1229 --- docs/narr/assets.rst | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs/narr') diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst index b0a8d18b0..fec55ce7c 100644 --- a/docs/narr/assets.rst +++ b/docs/narr/assets.rst @@ -526,3 +526,6 @@ files. Any software which uses the :func:`pkg_resources.get_resource_string` APIs will obtain an overridden file when an override is used. +As of Pyramid 1.6, it is also possible to override an asset by supplying an +absolute path to a file or directory. This may be useful if the assets are +not distributed as part of a Python package. -- cgit v1.2.3 From 81719b800cfea1c6fd68427ea1d9c0a2f3e6c1dd Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 12 Aug 2014 21:56:26 -0500 Subject: Docs: Make clear that a userid need not be a principal. --- docs/narr/security.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 8db23a33b..57d7ac38f 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -611,9 +611,9 @@ that implements the following interface: def effective_principals(self, request): """ Return a sequence representing the effective principals - including the userid and any groups belonged to by the current - user, including 'system' groups such as - ``pyramid.security.Everyone`` and + typically including the userid and any groups belonged to + by the current user, always including 'system' groups such + as ``pyramid.security.Everyone`` and ``pyramid.security.Authenticated``. """ def remember(self, request, principal, **kw): -- cgit v1.2.3 From c7afe4e43ab19a5e8274988fe8dd004c04c160a1 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 12 Aug 2014 22:10:03 -0500 Subject: Security: Change "principal" argument in security.remember() to "userid". Make the change througout the authentication policies, etc. as well. --- docs/narr/security.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 57d7ac38f..16718cfa4 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -104,9 +104,9 @@ For example: The above configuration enables a policy which compares the value of an "auth ticket" cookie passed in the request's environment which contains a reference -to a single :term:`principal` against the principals present in any -:term:`ACL` found in the resource tree when attempting to call some -:term:`view`. +to a single :term:`userid` and matches that userid's principals against the +principals present in any :term:`ACL` found in the resource tree when +attempting to call some :term:`view`. While it is possible to mix and match different authentication and authorization policies, it is an error to configure a Pyramid application @@ -616,11 +616,11 @@ that implements the following interface: as ``pyramid.security.Everyone`` and ``pyramid.security.Authenticated``. """ - def remember(self, request, principal, **kw): + def remember(self, request, userid, **kw): """ Return a set of headers suitable for 'remembering' the - principal named ``principal`` when set in a response. An - individual authentication policy and its consumers can decide - on the composition and meaning of **kw. """ + userid named ``userid`` when set in a response. An + individual authentication policy and its consumers can + decide on the composition and meaning of **kw. """ def forget(self, request): """ Return a set of headers suitable for 'forgetting' the -- cgit v1.2.3 From dc324784193a577bc039dcddb0651ef5ec9e6f57 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 12 Aug 2014 22:12:25 -0500 Subject: Docs: Make "userid" link to the glossary term. --- docs/narr/security.rst | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 16718cfa4..f3879d0ba 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -595,19 +595,21 @@ that implements the following interface: """ An object representing a Pyramid authentication policy. """ def authenticated_userid(self, request): - """ Return the authenticated userid or ``None`` if no - authenticated userid can be found. This method of the policy - should ensure that a record exists in whatever persistent store is - used related to the user (the user should not have been deleted); - if a record associated with the current id does not exist in a - persistent store, it should return ``None``.""" + """ Return the authenticated :term:`userid` or ``None`` if + no authenticated userid can be found. This method of the + policy should ensure that a record exists in whatever + persistent store is used related to the user (the user + should not have been deleted); if a record associated with + the current id does not exist in a persistent store, it + should return ``None``.""" def unauthenticated_userid(self, request): - """ Return the *unauthenticated* userid. This method performs the - same duty as ``authenticated_userid`` but is permitted to return the - userid based only on data present in the request; it needn't (and - shouldn't) check any persistent store to ensure that the user record - related to the request userid exists.""" + """ Return the *unauthenticated* userid. This method + performs the same duty as ``authenticated_userid`` but is + permitted to return the userid based only on data present + in the request; it needn't (and shouldn't) check any + persistent store to ensure that the user record related to + the request userid exists.""" def effective_principals(self, request): """ Return a sequence representing the effective principals -- cgit v1.2.3 From a0cba72fb9925a1476ebf0848fa6ae07bbea5840 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 12 Aug 2014 22:33:48 -0500 Subject: Docs: Include the concept of credentials in the high level security overview. --- docs/narr/security.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index f3879d0ba..29c62d9f3 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -13,6 +13,11 @@ authorization system can use the credentials in the :term:`request` along with the :term:`context` resource to determine if access will be allowed. Here's how it works at a high level: +- A user may or may not have previously visited the application and + supplied authentication credentials, including a :term:`userid`. If + so, the application may have called + :func:`pyramid.security.remember` to remember these. + - A :term:`request` is generated when a user visits the application. - Based on the request, a :term:`context` resource is located through @@ -25,7 +30,9 @@ allowed. Here's how it works at a high level: context as well as other attributes of the request. - If an :term:`authentication policy` is in effect, it is passed the - request; it returns some number of :term:`principal` identifiers. + request. Based on the request and the remembered (or lack of) + :term:`userid` and related credentials it returns some number of + :term:`principal` identifiers. - If an :term:`authorization policy` is in effect and the :term:`view configuration` associated with the view callable that was found has -- cgit v1.2.3 From 6bedf31e5275c2f2a33051a547aa1dc722aafa97 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 12 Aug 2014 23:05:35 -0500 Subject: Docs: Add resource tree into security overview. --- docs/narr/security.rst | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 29c62d9f3..e6bbff44e 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -20,6 +20,12 @@ allowed. Here's how it works at a high level: - A :term:`request` is generated when a user visits the application. +- If an :term:`authorization policy` is in effect the application uses + the request and it's :term:`root factory` to create a :ref:`resource tree + ` of :term:`contexts `. The resource + tree maps contexts to URLs and within the contexts the application + puts declarations which authorize access. + - Based on the request, a :term:`context` resource is located through :term:`resource location`. A context is located differently depending on whether the application uses :term:`traversal` or :term:`URL dispatch`, but -- cgit v1.2.3 From 03e95958a9c2b9042e55bc55e4cdb193649857ef Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 12 Aug 2014 23:42:20 -0500 Subject: Docs: Switched first 2 paragraphs of security overview. --- docs/narr/security.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index e6bbff44e..203962751 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -6,8 +6,18 @@ Security ======== -:app:`Pyramid` provides an optional declarative authorization system -that can prevent a :term:`view` from being invoked based on an +:app:`Pyramid` provides an optional, declarative, security system. +Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly +separates authentication and authorization. Authentication is merely the +mechanism by which credentials provided in the :term:`request` are +resolved to one or more :term:`principal` identifiers. These identifiers +represent the users and groups in effect during the request. +Authorization then determines access based on the :term:`principal` +identifiers, the :term:`view callable` being invoked, and the +:term:`context` resource. + +The :app:`Pyramid` authorization system +can prevent a :term:`view` from being invoked based on an :term:`authorization policy`. Before a view is invoked, the authorization system can use the credentials in the :term:`request` along with the :term:`context` resource to determine if access will be @@ -54,14 +64,6 @@ allowed. Here's how it works at a high level: - If the authorization policy denies access, the view callable is not invoked; instead the :term:`forbidden view` is invoked. -Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly -separates authentication and authorization. Authentication is merely the -mechanism by which credentials provided in the :term:`request` are -resolved to one or more :term:`principal` identifiers. These identifiers -represent the users and groups in effect during the request. -Authorization then determines access based on the :term:`principal` -identifiers, the :term:`view callable` being invoked, and the -:term:`context` resource. Authorization is enabled by modifying your application to include an :term:`authentication policy` and :term:`authorization policy`. -- cgit v1.2.3 From fe83c6bfdab16818cb434d95a09bd6510b43aa24 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 13 Aug 2014 10:48:22 -0500 Subject: some tweaks to the usage of userid in the docs --- docs/narr/security.rst | 69 ++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 30 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 203962751..2dc0c76af 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -7,14 +7,14 @@ Security ======== :app:`Pyramid` provides an optional, declarative, security system. -Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly -separates authentication and authorization. Authentication is merely the -mechanism by which credentials provided in the :term:`request` are -resolved to one or more :term:`principal` identifiers. These identifiers -represent the users and groups in effect during the request. -Authorization then determines access based on the :term:`principal` -identifiers, the :term:`view callable` being invoked, and the -:term:`context` resource. +Security in :app:`Pyramid` is separated into authentication and +authorization. The two systems communicate via :term:`principal` +identifiers. Authentication is merely the mechanism by which credentials +provided in the :term:`request` are resolved to one or more +:term:`principal` identifiers. These identifiers represent the users and +groups that are in effect during the request. Authorization then determines +access based on the :term:`principal` identifiers, the requested +:term:`permission`, and a :term:`context`. The :app:`Pyramid` authorization system can prevent a :term:`view` from being invoked based on an @@ -30,12 +30,6 @@ allowed. Here's how it works at a high level: - A :term:`request` is generated when a user visits the application. -- If an :term:`authorization policy` is in effect the application uses - the request and it's :term:`root factory` to create a :ref:`resource tree - ` of :term:`contexts `. The resource - tree maps contexts to URLs and within the contexts the application - puts declarations which authorize access. - - Based on the request, a :term:`context` resource is located through :term:`resource location`. A context is located differently depending on whether the application uses :term:`traversal` or :term:`URL dispatch`, but @@ -46,9 +40,9 @@ allowed. Here's how it works at a high level: context as well as other attributes of the request. - If an :term:`authentication policy` is in effect, it is passed the - request. Based on the request and the remembered (or lack of) - :term:`userid` and related credentials it returns some number of - :term:`principal` identifiers. + request. It will return some number of :term:`principal` identifiers. + To do this, the policy would need to determine the authenticated + :term:`userid` present in the request. - If an :term:`authorization policy` is in effect and the :term:`view configuration` associated with the view callable that was found has @@ -64,7 +58,6 @@ allowed. Here's how it works at a high level: - If the authorization policy denies access, the view callable is not invoked; instead the :term:`forbidden view` is invoked. - Authorization is enabled by modifying your application to include an :term:`authentication policy` and :term:`authorization policy`. :app:`Pyramid` comes with a variety of implementations of these @@ -119,9 +112,10 @@ For example: The above configuration enables a policy which compares the value of an "auth ticket" cookie passed in the request's environment which contains a reference -to a single :term:`userid` and matches that userid's principals against the -principals present in any :term:`ACL` found in the resource tree when -attempting to call some :term:`view`. +to a single :term:`userid` and matches that userid's +:term:`principals ` against the principals present in any +:term:`ACL` found in the resource tree when attempting to call some +:term:`view`. While it is possible to mix and match different authentication and authorization policies, it is an error to configure a Pyramid application @@ -616,7 +610,9 @@ that implements the following interface: persistent store is used related to the user (the user should not have been deleted); if a record associated with the current id does not exist in a persistent store, it - should return ``None``.""" + should return ``None``. + + """ def unauthenticated_userid(self, request): """ Return the *unauthenticated* userid. This method @@ -624,24 +620,37 @@ that implements the following interface: permitted to return the userid based only on data present in the request; it needn't (and shouldn't) check any persistent store to ensure that the user record related to - the request userid exists.""" + the request userid exists. + + This method is intended primarily a helper to assist the + ``authenticated_userid`` method in pulling credentials out + of the request data, abstracting away the specific headers, + query strings, etc that are used to authenticate the request. + + """ def effective_principals(self, request): """ Return a sequence representing the effective principals - typically including the userid and any groups belonged to - by the current user, always including 'system' groups such + typically including the :term:`userid` and any groups belonged + to by the current user, always including 'system' groups such as ``pyramid.security.Everyone`` and - ``pyramid.security.Authenticated``. """ + ``pyramid.security.Authenticated``. + + """ def remember(self, request, userid, **kw): """ Return a set of headers suitable for 'remembering' the - userid named ``userid`` when set in a response. An + :term:`userid` named ``userid`` when set in a response. An individual authentication policy and its consumers can - decide on the composition and meaning of **kw. """ - + decide on the composition and meaning of **kw. + + """ + def forget(self, request): """ Return a set of headers suitable for 'forgetting' the - current user on subsequent requests. """ + current user on subsequent requests. + + """ After you do so, you can pass an instance of such a class into the :class:`~pyramid.config.Configurator.set_authentication_policy` method -- cgit v1.2.3 From b542a4d723e5e994f693884618878186b94fa51c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 17 Nov 2014 01:10:11 -0600 Subject: improve the docs for absolute path overrides --- docs/narr/assets.rst | 65 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 9 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst index 74708ff3e..fc908c2b4 100644 --- a/docs/narr/assets.rst +++ b/docs/narr/assets.rst @@ -276,15 +276,62 @@ to put static media on a separate webserver during production (if the ``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is a URL), while keeping static media package-internal and served by the development webserver during development (if the ``name`` argument to -:meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). To -create such a circumstance, we suggest using the -:attr:`pyramid.registry.Registry.settings` API in conjunction with a setting -in the application ``.ini`` file named ``media_location``. Then set the -value of ``media_location`` to either a prefix or a URL depending on whether -the application is being run in development or in production (use a different -``.ini`` file for production than you do for development). This is just a -suggestion for a pattern; any setting name other than ``media_location`` -could be used. +:meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). + +For example, we may define a :ref:`custom setting ` +named ``media_location`` which we can set to an external URL in production +when our assets are hosted on a CDN. + +.. code-block:: python + :linenos: + + media_location = settings.get('media_location', 'static') + + config = Configurator(settings=settings) + config.add_static_view(path='myapp:static', name=media_location) + +Now we can optionally define the setting in our ini file: + +.. code-block:: ini + :linenos: + + # production.ini + [app:main] + use = egg:myapp#main + + media_location = http://static.example.com/ + +It is also possible to serve assets that live outside of the source by +referring to an absolute path on the filesystem. There are two ways to +accomplish this. + +First, :meth:`~pyramid.config.Configurator.add_static_view` +supports taking an absolute path directly instead of an asset spec. This works +as expected, looking in the file or folder of files and serving them up at +some URL within your application or externally. Unfortunately, this technique +has a drawback that it is not possible to use the +:meth:`~pyramid.request.Request.static_url` method to generate URLs, since it +works based on an asset spec. + +The second approach, available in Pyramid 1.6+, uses the asset overriding +APIs described in the :ref:`overriding_assets_section` section. It is then +possible to configure a "dummy" package which then serves its file or folder +from an absolute path. + +.. code-block:: python + + config.add_static_view(path='myapp:static_images', name='static') + config.override_asset(to_override='myapp:static_images/', + override_with='/abs/path/to/images/') + +From this configuration it is now possible to use +:meth:`~pyramid.request.Request.static_url` to generate URLs to the data +in the folder by doing something like +``request.static_url('myapp:static_images/foo.png')``. While it is not +necessary that the ``static_images`` file or folder actually exist in the +``myapp`` package, it is important that the ``myapp`` portion points to a +valid package. If the folder does exist then the overriden folder is given +priority if the file's name exists in both locations. .. index:: single: Cache Busting -- cgit v1.2.3 From 370862eb748f74dacee6b2bb1a5a2e35f865018a Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 22 Nov 2014 23:31:50 -0800 Subject: add request processing diagram to docs/narr/router.rst --- docs/narr/router.rst | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs/narr') diff --git a/docs/narr/router.rst b/docs/narr/router.rst index ac3deefdc..745c2faa1 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -9,6 +9,9 @@ Request Processing ================== +.. image:: ../_static/pyramid_router.svg + :alt: Request Processing + Once a :app:`Pyramid` application is up and running, it is ready to accept requests and return responses. What happens from the time a :term:`WSGI` request enters a :app:`Pyramid` application through to the point that -- cgit v1.2.3 From 138706a24bd8e7051c60942c2789d8c16b4ca2ed Mon Sep 17 00:00:00 2001 From: Matt Russell Date: Wed, 19 Nov 2014 23:06:17 +0000 Subject: Include code examples for integration and functional tests in docs #1001 Wrap lines as per convention. --- docs/narr/MyProject/myproject/tests.py | 37 +++++++++ docs/narr/MyProject/setup.py | 45 +++++++---- docs/narr/testing.rst | 142 +++++++++++++++------------------ 3 files changed, 131 insertions(+), 93 deletions(-) (limited to 'docs/narr') diff --git a/docs/narr/MyProject/myproject/tests.py b/docs/narr/MyProject/myproject/tests.py index 64dcab1d5..8c60407e5 100644 --- a/docs/narr/MyProject/myproject/tests.py +++ b/docs/narr/MyProject/myproject/tests.py @@ -15,3 +15,40 @@ class ViewTests(unittest.TestCase): request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['project'], 'MyProject') + +class ViewIntegrationTests(unittest.TestCase): + def setUp(self): + """ This sets up the application registry with the + registrations your application declares in its ``includeme`` + function. + """ + self.config = testing.setUp() + self.config.include('myproject') + + def tearDown(self): + """ Clear out the application registry """ + testing.tearDown() + + def test_my_view(self): + from myproject.views import my_view + request = testing.DummyRequest() + result = my_view(request) + self.assertEqual(result.status, '200 OK') + body = result.app_iter[0] + self.assertTrue('Welcome to' in body) + self.assertEqual(len(result.headerlist), 2) + self.assertEqual(result.headerlist[0], + ('Content-Type', 'text/html; charset=UTF-8')) + self.assertEqual(result.headerlist[1], ('Content-Length', + str(len(body)))) + +class FunctionalTests(unittest.TestCase): + def setUp(self): + from myproject import main + app = main({}) + from webtest import TestApp + self.testapp = TestApp(app) + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertTrue('Pyramid' in res.body) diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py index 8c019af51..9f34540a7 100644 --- a/docs/narr/MyProject/setup.py +++ b/docs/narr/MyProject/setup.py @@ -1,30 +1,42 @@ -import os +"""Setup for the MyProject package. +""" +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 = [ +HERE = os.path.abspath(os.path.dirname(__file__)) + + +with open(os.path.join(HERE, 'README.txt')) as fp: + README = fp.read() + + +with open(os.path.join(HERE, 'CHANGES.txt')) as fp: + CHANGES = fp.read() + + +REQUIRES = [ 'pyramid', 'pyramid_chameleon', 'pyramid_debugtoolbar', 'waitress', ] +TESTS_REQUIRE = [ + 'webtest' + ] + setup(name='MyProject', version='0.0', description='MyProject', long_description=README + '\n\n' + CHANGES, classifiers=[ - "Programming Language :: Python", - "Framework :: Pyramid", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], + 'Programming Language :: Python', + 'Framework :: Pyramid', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], author='', author_email='', url='', @@ -32,11 +44,10 @@ setup(name='MyProject', packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=requires, - tests_require=requires, - test_suite="myproject", + install_requires=REQUIRES, + tests_require=TESTS_REQUIRE, + test_suite='myproject', entry_points="""\ [paste.app_factory] main = myproject:main - """, - ) + """) diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index e001ad81c..3620f5e11 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -128,8 +128,9 @@ functions accepts various arguments that influence the environment of the test. See the :ref:`testing_module` API for information about the extra arguments supported by these functions. -If you also want to make :func:`~pyramid.threadlocal.get_current_request` return something -other than ``None`` during the course of a single test, you can pass a +If you also want to make :func:`~pyramid.threadlocal.get_current_request` +return something other than ``None`` during the course of a single test, you +can pass a :term:`request` object into the :func:`pyramid.testing.setUp` within the ``setUp`` method of your test: @@ -333,66 +334,49 @@ Creating Integration Tests -------------------------- In :app:`Pyramid`, a *unit test* typically relies on "mock" or "dummy" -implementations to give the code under test only enough context to run. +implementations to give the code under test enough context to run. "Integration testing" implies another sort of testing. In the context of a -:app:`Pyramid` integration test, the test logic tests the functionality of -some code *and* its integration with the rest of the :app:`Pyramid` +:app:`Pyramid` integration test, the test logic exercises the functionality of +the code under test *and* its integration with the rest of the :app:`Pyramid` framework. -In :app:`Pyramid` applications that are plugins to Pyramid, you can create an -integration test by including its ``includeme`` function via -:meth:`pyramid.config.Configurator.include` in the test's setup code. This -causes the entire :app:`Pyramid` environment to be set up and torn down as if -your application was running "for real". This is a heavy-hammer way of -making sure that your tests have enough context to run properly, and it tests -your code's integration with the rest of :app:`Pyramid`. +Creating an integration test for a :app:`Pyramid` application usually means +invoking the application's ``includeme`` function via +:meth:`pyramid.config.Configurator.include` within the test's setup code. This +causes the entire :app:`Pyramid` environment to be set up, simulating what +happens when your application is run "for real". This is a heavy-hammer way of +making sure that your tests have enough context to run properly, and tests your +code's integration with the rest of :app:`Pyramid`. -Let's demonstrate this by showing an integration test for a view. The below -test assumes that your application's package name is ``myapp``, and that -there is a ``views`` module in the app with a function with the name -``my_view`` in it that returns the response 'Welcome to this application' -after accessing some values that require a fully set up environment. +.. seealso:: -.. code-block:: python - :linenos: + See more information about :app:`Pyramid`'s ``includme`` function. - import unittest +Let's demonstrate this by showing an integration test for a view. - from pyramid import testing +Given the following view definition, which assumes that your application's +:term:`package` name is ``myproject``, and within that :term:`package` there +exists a module ``views``, which in turn contains a :term:`view` function named +``my_view``: - class ViewIntegrationTests(unittest.TestCase): - def setUp(self): - """ This sets up the application registry with the - registrations your application declares in its ``includeme`` - function. - """ - import myapp - self.config = testing.setUp() - self.config.include('myapp') + .. literalinclude:: MyProject/myproject/views.py + :linenos: + :lines: 1-6 + :language: python - def tearDown(self): - """ Clear out the application registry """ - testing.tearDown() +You'd then create a ``tests`` module within your ``myproject`` package, +containing the following test code: - def test_my_view(self): - from myapp.views import my_view - request = testing.DummyRequest() - result = my_view(request) - self.assertEqual(result.status, '200 OK') - body = result.app_iter[0] - self.assertTrue('Welcome to' in body) - self.assertEqual(len(result.headerlist), 2) - self.assertEqual(result.headerlist[0], - ('Content-Type', 'text/html; charset=UTF-8')) - self.assertEqual(result.headerlist[1], ('Content-Length', - str(len(body)))) - -Unless you cannot avoid it, you should prefer writing unit tests that use the -:class:`~pyramid.config.Configurator` API to set up the right "mock" -registrations rather than creating an integration test. Unit tests will run -faster (because they do less for each test) and the result of a unit test is -usually easier to make assertions about. + .. literalinclude:: MyProject/myproject/tests.py + :linenos: + :pyobject: ViewIntegrationTests + :language: python + +Writing unit tests that use the :class:`~pyramid.config.Configurator` API to +set up the right "mock" registrations is often preferred to creating +integration tests. Unit tests will run faster (because they do less for each +test) and are usually easier to reason about. .. index:: single: functional tests @@ -404,34 +388,40 @@ Creating Functional Tests Functional tests test your literal application. -The below test assumes that your application's package name is ``myapp``, and -that there is a view that returns an HTML body when the root URL is invoked. -It further assumes that you've added a ``tests_require`` dependency on the -``WebTest`` package within your ``setup.py`` file. :term:`WebTest` is a -functional testing package written by Ian Bicking. +In Pyramid, functional tests are typically written using the :term:`WebTest` +package, which provides APIs for invoking HTTP(S) requests to your application. -.. code-block:: python - :linenos: +Regardless of which testing :term:`package` you use, ensure to add a +``tests_require`` dependency on that package to to your application's +``setup.py`` file: - import unittest + .. literalinclude:: MyProject/setup.py + :linenos: + :emphasize-lines: 26-28,48 + :language: python - class FunctionalTests(unittest.TestCase): - def setUp(self): - from myapp import main - app = main({}) - from webtest import TestApp - self.testapp = TestApp(app) - - def test_root(self): - res = self.testapp.get('/', status=200) - self.assertTrue('Pyramid' in res.body) - -When this test is run, each test creates a "real" WSGI application using the -``main`` function in your ``myapp.__init__`` module and uses :term:`WebTest` -to wrap that WSGI application. It assigns the result to ``self.testapp``. -In the test named ``test_root``, we use the testapp's ``get`` method to -invoke the root URL. We then assert that the returned HTML has the string -``Pyramid`` in it. +Assuming your :term:`package` is named ``myproject``, which contains a +``views`` module, which in turn contains a :term:`view` function ``my_view`` +that returns a HTML body when the root URL is invoked: + + .. literalinclude:: MyProject/myproject/views.py + :linenos: + :language: python + +Then the following example functional test (shown below) demonstrates invoking +the :term:`view` shown above: + + .. literalinclude:: MyProject/myproject/tests.py + :linenos: + :pyobject: FunctionalTests + :language: python + +When this test is run, each test method creates a "real" :term:`WSGI` +application using the ``main`` function in your ``myproject.__init__`` module, +using :term:`WebTest` to wrap that WSGI application. It assigns the result to +``self.testapp``. In the test named ``test_root``. The ``TestApp``'s ``get`` +method is used to invoke the root URL. Finally, an assertion is made that the +returned HTML contains the text ``MyProject``. See the :term:`WebTest` documentation for further information about the methods available to a :class:`webtest.app.TestApp` instance. -- cgit v1.2.3 From 9c94e129f1bbb753317deba7ea5f790db13e0709 Mon Sep 17 00:00:00 2001 From: Matt Russell Date: Tue, 25 Nov 2014 20:59:18 +0000 Subject: Tweak seealso for the includeme function. --- docs/narr/testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/narr') diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index 3620f5e11..ecda57489 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -351,7 +351,7 @@ code's integration with the rest of :app:`Pyramid`. .. seealso:: - See more information about :app:`Pyramid`'s ``includme`` function. + See also :ref:`including_configuration` Let's demonstrate this by showing an integration test for a view. -- cgit v1.2.3 From 3408269bd291b771efef8e54f039038fc5b59a26 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 1 Dec 2014 13:40:39 -0800 Subject: - rename pyramid_router.svg to pyramid_request_processing.svg to be consistent with its content - add source files for future modifications --- docs/narr/router.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/narr') diff --git a/docs/narr/router.rst b/docs/narr/router.rst index 745c2faa1..e82b66801 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -9,7 +9,7 @@ Request Processing ================== -.. image:: ../_static/pyramid_router.svg +.. image:: ../_static/pyramid_request_processing.svg :alt: Request Processing Once a :app:`Pyramid` application is up and running, it is ready to accept -- cgit v1.2.3