diff options
| author | Michael Merickel <michael@merickel.org> | 2021-01-15 13:31:48 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-15 13:31:48 -0600 |
| commit | 074f4f3eeec94b133293c0d1d0fa81d681b08e37 (patch) | |
| tree | 2c0b41982a2298592a180887a924b67fa16d65e0 /docs | |
| parent | 837358ee6be552fd2f990d1ed8d6ea9e1c98d583 (diff) | |
| parent | b0dd658429367dd5e3cd99973bcc9a6763dcc5e7 (diff) | |
| download | pyramid-074f4f3eeec94b133293c0d1d0fa81d681b08e37.tar.gz pyramid-074f4f3eeec94b133293c0d1d0fa81d681b08e37.tar.bz2 pyramid-074f4f3eeec94b133293c0d1d0fa81d681b08e37.zip | |
Merge pull request #3648 from Pylons/prep-2.0-docs
More 2.0 docs prep
Diffstat (limited to 'docs')
63 files changed, 870 insertions, 413 deletions
diff --git a/docs/narr/myproject/.gitignore b/docs/narr/myproject/.gitignore index c612e59f2..e9336274d 100644 --- a/docs/narr/myproject/.gitignore +++ b/docs/narr/myproject/.gitignore @@ -11,7 +11,7 @@ dist/ nosetests.xml env*/ tmp/ -Data.fs* +Data*.fs* *.sublime-project *.sublime-workspace .*.sw? diff --git a/docs/narr/myproject/README.txt b/docs/narr/myproject/README.txt index 6c5a0fee0..0a71384dc 100644 --- a/docs/narr/myproject/README.txt +++ b/docs/narr/myproject/README.txt @@ -4,15 +4,16 @@ myproject Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd myproject -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/narr/myproject/pytest.ini b/docs/narr/myproject/pytest.ini index 5c8c59068..074237039 100644 --- a/docs/narr/myproject/pytest.ini +++ b/docs/narr/myproject/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = myproject diff --git a/docs/narr/myproject/tests/conftest.py b/docs/narr/myproject/tests/conftest.py index 296205927..ec09cdb2d 100644 --- a/docs/narr/myproject/tests/conftest.py +++ b/docs/narr/myproject/tests/conftest.py @@ -1,7 +1,7 @@ import os from pyramid.paster import get_appsettings from pyramid.scripting import prepare -from pyramid.testing import DummyRequest +from pyramid.testing import DummyRequest, testConfig import pytest import webtest @@ -41,29 +41,36 @@ def app_request(app): drawbacks in tests as it's harder to mock data and is heavier. """ - env = prepare(registry=app.registry) - request = env['request'] - request.host = 'example.com' - - yield request - env['closer']() + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request @pytest.fixture -def dummy_request(app): +def dummy_request(): """ A lightweight dummy request. - This request is ultra-lightweight and should be used only when the - request itself is not a large focus in the call-stack. - - It is way easier to mock and control side-effects using this object. + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: - It does not have request extensions applied. - Threadlocals are not properly pushed. """ request = DummyRequest() - request.registry = app.registry request.host = 'example.com' return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/narr/project.rst b/docs/narr/project.rst index ee75587e9..31dce9265 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -208,12 +208,7 @@ Elided output from a run of this command on Unix is shown below: .. code-block:: bash Running setup.py develop for myproject - Successfully installed Jinja2-2.10.3 Mako-1.1.0 MarkupSafe-1.1.1 \ - PasteDeploy-2.0.1 Pygments-2.5.2 hupper-1.9.1 myproject plaster-1.0 \ - plaster-pastedeploy-0.7 pyramid-1.10.4 pyramid-debugtoolbar-4.5.2 \ - pyramid-jinja2-2.8 pyramid-mako-1.1.0 repoze.lru-0.7 \ - translationstring-1.3 venusian-3.0.0 waitress-1.4.2 webob-1.8.5 \ - zope.deprecation-4.4.0 zope.interface-4.7.1 + Successfully installed Jinja2-2.11.2 Mako-1.1.3 MarkupSafe-1.1.1 PasteDeploy-2.1.1 Pygments-2.7.3 hupper-1.10.2 myproject plaster-1.0 plaster-pastedeploy-0.7 pyramid-1.10.5 pyramid-debugtoolbar-4.9 pyramid-jinja2-2.8 pyramid-mako-1.1.0 repoze.lru-0.7 translationstring-1.4 venusian-3.0.0 waitress-1.4.4 webob-1.8.6 zope.deprecation-4.4.0 zope.interface-5.2.0 This will install a :term:`distribution` representing your project into the virtual environment interpreter's library set so it can be found by ``import`` @@ -555,6 +550,7 @@ The ``myproject`` project we've generated has the following directory structure: │ └── notfound.py ├── production.ini ├── pytest.ini + ├── setup.py ├── testing.ini └── tests ├── __init__.py @@ -566,8 +562,8 @@ The ``myproject`` project we've generated has the following directory structure: .. index:: single: tests -``test_it.py`` -~~~~~~~~~~~~~~ +``tests`` package +~~~~~~~~~~~~~~~~~ The ``conftest.py``, ``test_functional.py``, and ``test_views.py`` modules in the ``tests`` package includes tests for your application. diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index c4ab0b3e8..be53a3344 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -682,7 +682,7 @@ Yikes! We got this far and we haven't yet discussed tests. This is particularly egregious, as Pyramid has had a deep commitment to full test coverage since before its release. -Our ``pyramid-cookiecutter-starter`` cookiecutter generated a ``test_it.py`` module inside the ``tests`` package with two unit tests and two functional tests in it. +Our ``pyramid-cookiecutter-starter`` cookiecutter generated ``conftest.py``, ``test_functional.py``, and ``test_views.py`` modules inside the ``tests`` package with two unit tests and two functional tests in it. It also configured ``setup.py`` with test requirements: ``pytest`` as the test runner, ``WebTest`` for running view tests, and the ``pytest-cov`` tool which yells at us for code that isn't tested: @@ -708,29 +708,34 @@ This yields the following output. .. code-block:: text =========================== test session starts =========================== - platform darwin -- Python 3.7.3, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 - rootdir: /<somepath>/hello_world, inifile: pytest.ini, testpaths: hello_world, tests - plugins: cov-2.8.1 + platform darwin -- Python 3.9.0, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 + rootdir: /<somepath>/hello_world, configfile: pytest.ini, testpaths: hello_world, tests + plugins: cov-2.10.1 collected 4 items - tests/test_it.py .... [100%] + tests/test_functional.py .. [ 50%] + tests/test_views.py .. [100%] - ---------- coverage: platform darwin, python 3.7.3-final-0 ----------- + ---------- coverage: platform darwin, python 3.9.0-final-0 ----------- Name Stmts Miss Cover Missing ------------------------------------------------------------- hello_world/__init__.py 7 0 100% hello_world/routes.py 3 0 100% hello_world/views/__init__.py 0 0 100% - hello_world/views/default.py 3 0 100% - hello_world/views/notfound.py 4 0 100% + hello_world/views/default.py 4 0 100% + hello_world/views/notfound.py 5 0 100% ------------------------------------------------------------- - TOTAL 17 0 100% + TOTAL 19 0 100% ======================== 4 passed in 0.65 seconds ========================= -Our tests passed, and its coverage is complete. What did our test look like? +Our tests passed, and its coverage is complete. What did our tests look like? -.. literalinclude:: quick_tour/package/tests/test_it.py +.. literalinclude:: quick_tour/package/tests/test_functional.py + :language: python + :linenos: + +.. literalinclude:: quick_tour/package/tests/test_views.py :language: python :linenos: @@ -755,7 +760,7 @@ logging for you to some reasonable defaults. You then see messages sent by Pyramid (for example, when a new request comes in). Maybe you would like to log messages in your code? In your Python module, -import and set up the logging in your ``views.py``: +import and set up the logging in your ``views/default.py``: .. literalinclude:: quick_tour/logging/hello_world/views/default.py :language: python diff --git a/docs/quick_tour/logging/tests/conftest.py b/docs/quick_tour/logging/tests/conftest.py new file mode 100644 index 000000000..adc1e0f3f --- /dev/null +++ b/docs/quick_tour/logging/tests/conftest.py @@ -0,0 +1,76 @@ +import os +from pyramid.paster import get_appsettings +from pyramid.scripting import prepare +from pyramid.testing import DummyRequest, testConfig +import pytest +import webtest + +from hello_world import main + + +def pytest_addoption(parser): + parser.addoption('--ini', action='store', metavar='INI_FILE') + +@pytest.fixture(scope='session') +def ini_file(request): + # potentially grab this path from a pytest option + return os.path.abspath(request.config.option.ini or 'testing.ini') + +@pytest.fixture(scope='session') +def app_settings(ini_file): + return get_appsettings(ini_file) + +@pytest.fixture(scope='session') +def app(app_settings): + return main({}, **app_settings) + +@pytest.fixture +def testapp(app): + testapp = webtest.TestApp(app, extra_environ={ + 'HTTP_HOST': 'example.com', + }) + + return testapp + +@pytest.fixture +def app_request(app): + """ + A real request. + + This request is almost identical to a real request but it has some + drawbacks in tests as it's harder to mock data and is heavier. + + """ + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request + +@pytest.fixture +def dummy_request(): + """ + A lightweight dummy request. + + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: + + - It does not have request extensions applied. + - Threadlocals are not properly pushed. + + """ + request = DummyRequest() + request.host = 'example.com' + + return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/quick_tour/logging/tests/test_functional.py b/docs/quick_tour/logging/tests/test_functional.py new file mode 100644 index 000000000..bac5d63f4 --- /dev/null +++ b/docs/quick_tour/logging/tests/test_functional.py @@ -0,0 +1,7 @@ +def test_root(testapp): + res = testapp.get('/', status=200) + assert b'Pyramid' in res.body + +def test_notfound(testapp): + res = testapp.get('/badurl', status=404) + assert res.status_code == 404 diff --git a/docs/quick_tour/logging/tests/test_it.py b/docs/quick_tour/logging/tests/test_it.py deleted file mode 100644 index 90c6302fe..000000000 --- a/docs/quick_tour/logging/tests/test_it.py +++ /dev/null @@ -1,39 +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 hello_world.views.default import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['project'], 'hello_world') - - def test_notfound_view(self): - from hello_world.views.notfound import notfound_view - request = testing.DummyRequest() - info = notfound_view(request) - self.assertEqual(info, {}) - - -class FunctionalTests(unittest.TestCase): - def setUp(self): - from hello_world 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) - - def test_notfound(self): - res = self.testapp.get('/badurl', status=404) - self.assertTrue(res.status_code == 404) diff --git a/docs/quick_tour/logging/tests/test_views.py b/docs/quick_tour/logging/tests/test_views.py new file mode 100644 index 000000000..0b019fe82 --- /dev/null +++ b/docs/quick_tour/logging/tests/test_views.py @@ -0,0 +1,13 @@ +from hello_world.views.default import my_view +from hello_world.views.notfound import notfound_view + + +def test_my_view(app_request): + info = my_view(app_request) + assert app_request.response.status_int == 200 + assert info['project'] == 'hello_world' + +def test_notfound_view(app_request): + info = notfound_view(app_request) + assert app_request.response.status_int == 404 + assert info == {} diff --git a/docs/quick_tour/package/tests/conftest.py b/docs/quick_tour/package/tests/conftest.py new file mode 100644 index 000000000..adc1e0f3f --- /dev/null +++ b/docs/quick_tour/package/tests/conftest.py @@ -0,0 +1,76 @@ +import os +from pyramid.paster import get_appsettings +from pyramid.scripting import prepare +from pyramid.testing import DummyRequest, testConfig +import pytest +import webtest + +from hello_world import main + + +def pytest_addoption(parser): + parser.addoption('--ini', action='store', metavar='INI_FILE') + +@pytest.fixture(scope='session') +def ini_file(request): + # potentially grab this path from a pytest option + return os.path.abspath(request.config.option.ini or 'testing.ini') + +@pytest.fixture(scope='session') +def app_settings(ini_file): + return get_appsettings(ini_file) + +@pytest.fixture(scope='session') +def app(app_settings): + return main({}, **app_settings) + +@pytest.fixture +def testapp(app): + testapp = webtest.TestApp(app, extra_environ={ + 'HTTP_HOST': 'example.com', + }) + + return testapp + +@pytest.fixture +def app_request(app): + """ + A real request. + + This request is almost identical to a real request but it has some + drawbacks in tests as it's harder to mock data and is heavier. + + """ + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request + +@pytest.fixture +def dummy_request(): + """ + A lightweight dummy request. + + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: + + - It does not have request extensions applied. + - Threadlocals are not properly pushed. + + """ + request = DummyRequest() + request.host = 'example.com' + + return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/quick_tour/package/tests/test_functional.py b/docs/quick_tour/package/tests/test_functional.py new file mode 100644 index 000000000..bac5d63f4 --- /dev/null +++ b/docs/quick_tour/package/tests/test_functional.py @@ -0,0 +1,7 @@ +def test_root(testapp): + res = testapp.get('/', status=200) + assert b'Pyramid' in res.body + +def test_notfound(testapp): + res = testapp.get('/badurl', status=404) + assert res.status_code == 404 diff --git a/docs/quick_tour/package/tests/test_it.py b/docs/quick_tour/package/tests/test_it.py deleted file mode 100644 index 90c6302fe..000000000 --- a/docs/quick_tour/package/tests/test_it.py +++ /dev/null @@ -1,39 +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 hello_world.views.default import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['project'], 'hello_world') - - def test_notfound_view(self): - from hello_world.views.notfound import notfound_view - request = testing.DummyRequest() - info = notfound_view(request) - self.assertEqual(info, {}) - - -class FunctionalTests(unittest.TestCase): - def setUp(self): - from hello_world 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) - - def test_notfound(self): - res = self.testapp.get('/badurl', status=404) - self.assertTrue(res.status_code == 404) diff --git a/docs/quick_tour/package/tests/test_views.py b/docs/quick_tour/package/tests/test_views.py new file mode 100644 index 000000000..0b019fe82 --- /dev/null +++ b/docs/quick_tour/package/tests/test_views.py @@ -0,0 +1,13 @@ +from hello_world.views.default import my_view +from hello_world.views.notfound import notfound_view + + +def test_my_view(app_request): + info = my_view(app_request) + assert app_request.response.status_int == 200 + assert info['project'] == 'hello_world' + +def test_notfound_view(app_request): + info = notfound_view(app_request) + assert app_request.response.status_int == 404 + assert info == {} diff --git a/docs/quick_tour/sessions/tests/conftest.py b/docs/quick_tour/sessions/tests/conftest.py new file mode 100644 index 000000000..adc1e0f3f --- /dev/null +++ b/docs/quick_tour/sessions/tests/conftest.py @@ -0,0 +1,76 @@ +import os +from pyramid.paster import get_appsettings +from pyramid.scripting import prepare +from pyramid.testing import DummyRequest, testConfig +import pytest +import webtest + +from hello_world import main + + +def pytest_addoption(parser): + parser.addoption('--ini', action='store', metavar='INI_FILE') + +@pytest.fixture(scope='session') +def ini_file(request): + # potentially grab this path from a pytest option + return os.path.abspath(request.config.option.ini or 'testing.ini') + +@pytest.fixture(scope='session') +def app_settings(ini_file): + return get_appsettings(ini_file) + +@pytest.fixture(scope='session') +def app(app_settings): + return main({}, **app_settings) + +@pytest.fixture +def testapp(app): + testapp = webtest.TestApp(app, extra_environ={ + 'HTTP_HOST': 'example.com', + }) + + return testapp + +@pytest.fixture +def app_request(app): + """ + A real request. + + This request is almost identical to a real request but it has some + drawbacks in tests as it's harder to mock data and is heavier. + + """ + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request + +@pytest.fixture +def dummy_request(): + """ + A lightweight dummy request. + + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: + + - It does not have request extensions applied. + - Threadlocals are not properly pushed. + + """ + request = DummyRequest() + request.host = 'example.com' + + return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/quick_tour/sessions/tests/test_functional.py b/docs/quick_tour/sessions/tests/test_functional.py new file mode 100644 index 000000000..bac5d63f4 --- /dev/null +++ b/docs/quick_tour/sessions/tests/test_functional.py @@ -0,0 +1,7 @@ +def test_root(testapp): + res = testapp.get('/', status=200) + assert b'Pyramid' in res.body + +def test_notfound(testapp): + res = testapp.get('/badurl', status=404) + assert res.status_code == 404 diff --git a/docs/quick_tour/sessions/tests/test_it.py b/docs/quick_tour/sessions/tests/test_it.py deleted file mode 100644 index 90c6302fe..000000000 --- a/docs/quick_tour/sessions/tests/test_it.py +++ /dev/null @@ -1,39 +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 hello_world.views.default import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['project'], 'hello_world') - - def test_notfound_view(self): - from hello_world.views.notfound import notfound_view - request = testing.DummyRequest() - info = notfound_view(request) - self.assertEqual(info, {}) - - -class FunctionalTests(unittest.TestCase): - def setUp(self): - from hello_world 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) - - def test_notfound(self): - res = self.testapp.get('/badurl', status=404) - self.assertTrue(res.status_code == 404) diff --git a/docs/quick_tour/sessions/tests/test_views.py b/docs/quick_tour/sessions/tests/test_views.py new file mode 100644 index 000000000..0b019fe82 --- /dev/null +++ b/docs/quick_tour/sessions/tests/test_views.py @@ -0,0 +1,13 @@ +from hello_world.views.default import my_view +from hello_world.views.notfound import notfound_view + + +def test_my_view(app_request): + info = my_view(app_request) + assert app_request.response.status_int == 200 + assert info['project'] == 'hello_world' + +def test_notfound_view(app_request): + info = notfound_view(app_request) + assert app_request.response.status_int == 404 + assert info == {} diff --git a/docs/quick_tour/sqla_demo/.gitignore b/docs/quick_tour/sqla_demo/.gitignore index 1853d983c..e9336274d 100644 --- a/docs/quick_tour/sqla_demo/.gitignore +++ b/docs/quick_tour/sqla_demo/.gitignore @@ -11,7 +11,7 @@ dist/ nosetests.xml env*/ tmp/ -Data.fs* +Data*.fs* *.sublime-project *.sublime-workspace .*.sw? @@ -19,3 +19,4 @@ Data.fs* .DS_Store coverage test +*.sqlite diff --git a/docs/quick_tour/sqla_demo/README.txt b/docs/quick_tour/sqla_demo/README.txt index d00790492..3c3d7293e 100644 --- a/docs/quick_tour/sqla_demo/README.txt +++ b/docs/quick_tour/sqla_demo/README.txt @@ -4,15 +4,16 @@ sqla_demo Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd sqla_demo -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/quick_tour/sqla_demo/pytest.ini b/docs/quick_tour/sqla_demo/pytest.ini index e7fd17682..2c35d84ef 100644 --- a/docs/quick_tour/sqla_demo/pytest.ini +++ b/docs/quick_tour/sqla_demo/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = sqla_demo diff --git a/docs/quick_tour/sqla_demo/sqla_demo/__init__.py b/docs/quick_tour/sqla_demo/sqla_demo/__init__.py index 5c2ba5cc0..7edc0957d 100644 --- a/docs/quick_tour/sqla_demo/sqla_demo/__init__.py +++ b/docs/quick_tour/sqla_demo/sqla_demo/__init__.py @@ -5,8 +5,8 @@ def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ with Configurator(settings=settings) as config: - config.include('.models') config.include('pyramid_jinja2') config.include('.routes') + config.include('.models') config.scan() return config.make_wsgi_app() diff --git a/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py b/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py index 31aab9d26..71f9f36b5 100644 --- a/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py +++ b/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py @@ -65,13 +65,21 @@ def includeme(config): # use pyramid_retry to retry a request when transient exceptions occur config.include('pyramid_retry') - session_factory = get_session_factory(get_engine(settings)) + # hook to share the dbengine fixture in testing + dbengine = settings.get('dbengine') + if not dbengine: + dbengine = get_engine(settings) + + session_factory = get_session_factory(dbengine) config.registry['dbsession_factory'] = session_factory # make request.dbsession available for use in Pyramid - config.add_request_method( - # r.tm is the transaction manager used by pyramid_tm - lambda r: get_tm_session(session_factory, r.tm), - 'dbsession', - reify=True - ) + def dbsession(request): + # hook to share the dbsession fixture in testing + dbsession = request.environ.get('app.dbsession') + if dbsession is None: + # request.tm is the transaction manager used by pyramid_tm + dbsession = get_tm_session(session_factory, request.tm) + return dbsession + + config.add_request_method(dbsession, reify=True) diff --git a/docs/quick_tour/sqla_demo/sqla_demo/views/default.py b/docs/quick_tour/sqla_demo/sqla_demo/views/default.py index 3aadb905f..5476a9c7f 100644 --- a/docs/quick_tour/sqla_demo/sqla_demo/views/default.py +++ b/docs/quick_tour/sqla_demo/sqla_demo/views/default.py @@ -1,7 +1,6 @@ from pyramid.view import view_config from pyramid.response import Response - -from sqlalchemy.exc import DBAPIError +from sqlalchemy.exc import SQLAlchemyError from .. import models @@ -10,8 +9,8 @@ from .. import models def my_view(request): try: query = request.dbsession.query(models.MyModel) - one = query.filter(models.MyModel.name == 'one').first() - except DBAPIError: + one = query.filter(models.MyModel.name == 'one').one() + except SQLAlchemyError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'sqla_demo'} diff --git a/docs/quick_tour/sqla_demo/testing.ini b/docs/quick_tour/sqla_demo/testing.ini new file mode 100644 index 000000000..bccd51895 --- /dev/null +++ b/docs/quick_tour/sqla_demo/testing.ini @@ -0,0 +1,79 @@ +### +# app configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:sqla_demo + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +sqlalchemy.url = sqlite:///%(here)s/testing.sqlite + +retry.attempts = 3 + +[pshell] +setup = sqla_demo.pshell.setup + +### +# wsgi server configuration +### + +[alembic] +# path to migration scripts +script_location = sqla_demo/alembic +file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s +# file_template = %%(rev)s_%%(slug)s + +[server:main] +use = egg:waitress#main +listen = localhost:6543 + +### +# logging configuration +# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, sqla_demo, sqlalchemy, alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqla_demo] +level = DEBUG +handlers = +qualname = sqla_demo + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[logger_alembic] +level = WARN +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/quick_tour/sqla_demo/tests/conftest.py b/docs/quick_tour/sqla_demo/tests/conftest.py new file mode 100644 index 000000000..641527ca1 --- /dev/null +++ b/docs/quick_tour/sqla_demo/tests/conftest.py @@ -0,0 +1,132 @@ +import alembic +import alembic.config +import alembic.command +import os +from pyramid.paster import get_appsettings +from pyramid.scripting import prepare +from pyramid.testing import DummyRequest, testConfig +import pytest +import transaction +import webtest + +from sqla_demo import main +from sqla_demo import models +from sqla_demo.models.meta import Base + + +def pytest_addoption(parser): + parser.addoption('--ini', action='store', metavar='INI_FILE') + +@pytest.fixture(scope='session') +def ini_file(request): + # potentially grab this path from a pytest option + return os.path.abspath(request.config.option.ini or 'testing.ini') + +@pytest.fixture(scope='session') +def app_settings(ini_file): + return get_appsettings(ini_file) + +@pytest.fixture(scope='session') +def dbengine(app_settings, ini_file): + engine = models.get_engine(app_settings) + + alembic_cfg = alembic.config.Config(ini_file) + Base.metadata.drop_all(bind=engine) + alembic.command.stamp(alembic_cfg, None, purge=True) + + # run migrations to initialize the database + # depending on how we want to initialize the database from scratch + # we could alternatively call: + # Base.metadata.create_all(bind=engine) + # alembic.command.stamp(alembic_cfg, "head") + alembic.command.upgrade(alembic_cfg, "head") + + yield engine + + Base.metadata.drop_all(bind=engine) + alembic.command.stamp(alembic_cfg, None, purge=True) + +@pytest.fixture(scope='session') +def app(app_settings, dbengine): + return main({}, dbengine=dbengine, **app_settings) + +@pytest.fixture +def tm(): + tm = transaction.TransactionManager(explicit=True) + tm.begin() + tm.doom() + + yield tm + + tm.abort() + +@pytest.fixture +def dbsession(app, tm): + session_factory = app.registry['dbsession_factory'] + return models.get_tm_session(session_factory, tm) + +@pytest.fixture +def testapp(app, tm, dbsession): + # override request.dbsession and request.tm with our own + # externally-controlled values that are shared across requests but aborted + # at the end + testapp = webtest.TestApp(app, extra_environ={ + 'HTTP_HOST': 'example.com', + 'tm.active': True, + 'tm.manager': tm, + 'app.dbsession': dbsession, + }) + + return testapp + +@pytest.fixture +def app_request(app, tm, dbsession): + """ + A real request. + + This request is almost identical to a real request but it has some + drawbacks in tests as it's harder to mock data and is heavier. + + """ + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + + # without this, request.dbsession will be joined to the same transaction + # manager but it will be using a different sqlalchemy.orm.Session using + # a separate database transaction + request.dbsession = dbsession + request.tm = tm + + yield request + +@pytest.fixture +def dummy_request(tm, dbsession): + """ + A lightweight dummy request. + + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: + + - It does not have request extensions applied. + - Threadlocals are not properly pushed. + + """ + request = DummyRequest() + request.host = 'example.com' + request.dbsession = dbsession + request.tm = tm + + return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/quick_tour/sqla_demo/tests/test_functional.py b/docs/quick_tour/sqla_demo/tests/test_functional.py new file mode 100644 index 000000000..4045871da --- /dev/null +++ b/docs/quick_tour/sqla_demo/tests/test_functional.py @@ -0,0 +1,13 @@ +from sqla_demo import models + +def test_my_view_success(testapp, dbsession): + model = models.MyModel(name='one', value=55) + dbsession.add(model) + dbsession.flush() + + res = testapp.get('/', status=200) + assert res.body + +def test_notfound(testapp): + res = testapp.get('/badurl', status=404) + assert res.status_code == 404 diff --git a/docs/quick_tour/sqla_demo/tests/test_it.py b/docs/quick_tour/sqla_demo/tests/test_it.py deleted file mode 100644 index f0848564b..000000000 --- a/docs/quick_tour/sqla_demo/tests/test_it.py +++ /dev/null @@ -1,66 +0,0 @@ -import unittest - -from pyramid import testing - -import transaction - - -def dummy_request(dbsession): - return testing.DummyRequest(dbsession=dbsession) - - -class BaseTest(unittest.TestCase): - def setUp(self): - self.config = testing.setUp(settings={ - 'sqlalchemy.url': 'sqlite:///:memory:' - }) - self.config.include('sqla_demo.models') - settings = self.config.get_settings() - - from sqla_demo.models import ( - get_engine, - get_session_factory, - get_tm_session, - ) - - self.engine = get_engine(settings) - session_factory = get_session_factory(self.engine) - - self.session = get_tm_session(session_factory, transaction.manager) - - def init_database(self): - from sqla_demo.models.meta import Base - Base.metadata.create_all(self.engine) - - def tearDown(self): - from sqla_demo.models.meta import Base - - testing.tearDown() - transaction.abort() - Base.metadata.drop_all(self.engine) - - -class TestMyViewSuccessCondition(BaseTest): - - def setUp(self): - super().setUp() - self.init_database() - - from sqla_demo.models import MyModel - - model = MyModel(name='one', value=55) - self.session.add(model) - - def test_passing_view(self): - from sqla_demo.views.default import my_view - info = my_view(dummy_request(self.session)) - self.assertEqual(info['one'].name, 'one') - self.assertEqual(info['project'], 'sqla_demo') - - -class TestMyViewFailureCondition(BaseTest): - - def test_failing_view(self): - from sqla_demo.views.default import my_view - info = my_view(dummy_request(self.session)) - self.assertEqual(info.status_int, 500) diff --git a/docs/quick_tour/sqla_demo/tests/test_views.py b/docs/quick_tour/sqla_demo/tests/test_views.py new file mode 100644 index 000000000..b98d9af61 --- /dev/null +++ b/docs/quick_tour/sqla_demo/tests/test_views.py @@ -0,0 +1,23 @@ +from sqla_demo import models +from sqla_demo.views.default import my_view +from sqla_demo.views.notfound import notfound_view + + +def test_my_view_failure(app_request): + info = my_view(app_request) + assert info.status_int == 500 + +def test_my_view_success(app_request, dbsession): + model = models.MyModel(name='one', value=55) + dbsession.add(model) + dbsession.flush() + + info = my_view(app_request) + assert app_request.response.status_int == 200 + assert info['one'].name == 'one' + assert info['project'] == 'sqla_demo' + +def test_notfound_view(app_request): + info = notfound_view(app_request) + assert app_request.response.status_int == 404 + assert info == {} diff --git a/docs/quick_tutorial/cookiecutters/.gitignore b/docs/quick_tutorial/cookiecutters/.gitignore index 1853d983c..e9336274d 100644 --- a/docs/quick_tutorial/cookiecutters/.gitignore +++ b/docs/quick_tutorial/cookiecutters/.gitignore @@ -11,7 +11,7 @@ dist/ nosetests.xml env*/ tmp/ -Data.fs* +Data*.fs* *.sublime-project *.sublime-workspace .*.sw? @@ -19,3 +19,4 @@ Data.fs* .DS_Store coverage test +*.sqlite diff --git a/docs/quick_tutorial/cookiecutters/README.txt b/docs/quick_tutorial/cookiecutters/README.txt index 55c5dcec6..74a86520c 100644 --- a/docs/quick_tutorial/cookiecutters/README.txt +++ b/docs/quick_tutorial/cookiecutters/README.txt @@ -4,15 +4,16 @@ cc_starter Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd cc_starter -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/quick_tutorial/cookiecutters/pytest.ini b/docs/quick_tutorial/cookiecutters/pytest.ini index 515cc3cf0..b678eef00 100644 --- a/docs/quick_tutorial/cookiecutters/pytest.ini +++ b/docs/quick_tutorial/cookiecutters/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = cc_starter diff --git a/docs/quick_tutorial/cookiecutters/tests/conftest.py b/docs/quick_tutorial/cookiecutters/tests/conftest.py new file mode 100644 index 000000000..ec09cdb2d --- /dev/null +++ b/docs/quick_tutorial/cookiecutters/tests/conftest.py @@ -0,0 +1,76 @@ +import os +from pyramid.paster import get_appsettings +from pyramid.scripting import prepare +from pyramid.testing import DummyRequest, testConfig +import pytest +import webtest + +from myproject import main + + +def pytest_addoption(parser): + parser.addoption('--ini', action='store', metavar='INI_FILE') + +@pytest.fixture(scope='session') +def ini_file(request): + # potentially grab this path from a pytest option + return os.path.abspath(request.config.option.ini or 'testing.ini') + +@pytest.fixture(scope='session') +def app_settings(ini_file): + return get_appsettings(ini_file) + +@pytest.fixture(scope='session') +def app(app_settings): + return main({}, **app_settings) + +@pytest.fixture +def testapp(app): + testapp = webtest.TestApp(app, extra_environ={ + 'HTTP_HOST': 'example.com', + }) + + return testapp + +@pytest.fixture +def app_request(app): + """ + A real request. + + This request is almost identical to a real request but it has some + drawbacks in tests as it's harder to mock data and is heavier. + + """ + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request + +@pytest.fixture +def dummy_request(): + """ + A lightweight dummy request. + + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: + + - It does not have request extensions applied. + - Threadlocals are not properly pushed. + + """ + request = DummyRequest() + request.host = 'example.com' + + return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/quick_tutorial/cookiecutters/tests/test_functional.py b/docs/quick_tutorial/cookiecutters/tests/test_functional.py new file mode 100644 index 000000000..bac5d63f4 --- /dev/null +++ b/docs/quick_tutorial/cookiecutters/tests/test_functional.py @@ -0,0 +1,7 @@ +def test_root(testapp): + res = testapp.get('/', status=200) + assert b'Pyramid' in res.body + +def test_notfound(testapp): + res = testapp.get('/badurl', status=404) + assert res.status_code == 404 diff --git a/docs/quick_tutorial/cookiecutters/tests/test_it.py b/docs/quick_tutorial/cookiecutters/tests/test_it.py deleted file mode 100644 index 634abfdf2..000000000 --- a/docs/quick_tutorial/cookiecutters/tests/test_it.py +++ /dev/null @@ -1,39 +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 cc_starter.views.default import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['project'], 'cc_starter') - - def test_notfound_view(self): - from cc_starter.views.notfound import notfound_view - request = testing.DummyRequest() - info = notfound_view(request) - self.assertEqual(info, {}) - - -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) - - def test_notfound(self): - res = self.testapp.get('/badurl', status=404) - self.assertTrue(res.status_code == 404) diff --git a/docs/quick_tutorial/cookiecutters/tests/test_views.py b/docs/quick_tutorial/cookiecutters/tests/test_views.py new file mode 100644 index 000000000..1fd9db8ab --- /dev/null +++ b/docs/quick_tutorial/cookiecutters/tests/test_views.py @@ -0,0 +1,13 @@ +from myproject.views.default import my_view +from myproject.views.notfound import notfound_view + + +def test_my_view(app_request): + info = my_view(app_request) + assert app_request.response.status_int == 200 + assert info['project'] == 'myproject' + +def test_notfound_view(app_request): + info = notfound_view(app_request) + assert app_request.response.status_int == 404 + assert info == {} diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 9c685639d..2b700ee5b 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -164,7 +164,7 @@ Add an ACL Open ``tutorial/models/__init__.py`` and add the following import statement near the top: .. literalinclude:: src/authorization/tutorial/models/__init__.py - :lines: 4-7 + :lines: 3-6 :lineno-match: :language: python @@ -315,6 +315,14 @@ Launch a browser and visit each of the following URLs, checking that the result This always redirects to the ``view_page`` view of the ``FrontPage`` Page resource. It is executable by any user. +- http://localhost:6543/login invokes the ``login`` view, and a login form will be displayed. + On every page, there is a "Login" link in the upper right corner while the user is not authenticated, else it is a "Logout" link when the user is authenticated. + + Supplying the credentials with either the username ``editor`` and password ``editor`` will authenticate the user and grant access for that group. + + After logging in (as a result of hitting an edit or add page and submitting valid credentials), we will see a "Logout" link in the upper right hand corner. + When we click it, we are logged out, redirected back to the front page, and a "Login" link is shown in the upper right hand corner. + - http://localhost:6543/FrontPage invokes the ``view_page`` view of the ``FrontPage`` Page resource. This is because it is the :term:`default view` (a view without a ``name``) for ``Page`` resources. It is executable by any user. @@ -322,14 +330,11 @@ Launch a browser and visit each of the following URLs, checking that the result - http://localhost:6543/FrontPage/edit_page invokes the edit view for the FrontPage object. It is executable by only the ``editor`` user. If a different user (or the anonymous user) invokes it, then a login form will be displayed. - Supplying the credentials with the username ``editor`` and password ``editor`` will display the edit page form. + The ``editor`` user will see the edit page form. - http://localhost:6543/add_page/SomePageName invokes the add view for a page. It is executable by only the ``editor`` user. If a different user (or the anonymous user) invokes it, a login form will be displayed. - Supplying the credentials with the username ``editor``, password ``editor`` will display the edit page form. - -- After logging in (as a result of hitting an edit or add page and submitting the login form with the ``editor`` credentials), we will see a Logout link in the upper right hand corner. - When we click it, we are logged out, and redirected back to the front page. + The ``editor`` user will see the edit page form. - To generate a not found error, visit http://localhost:6543/wakawaka which will invoke the ``notfound_view`` view provided by the cookiecutter. diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst index 4de9b4b9c..7113c4bc2 100644 --- a/docs/tutorials/wiki/installation.rst +++ b/docs/tutorials/wiki/installation.rst @@ -185,20 +185,7 @@ The console will show ``pip`` checking for packages and installing missing packa .. code-block:: bash - Successfully installed BTrees-4.6.1 Chameleon-3.6.2 Mako-1.1.0 \ - MarkupSafe-1.1.1 PasteDeploy-2.0.1 Pygments-2.5.2 WebTest-2.0.33 \ - ZConfig-3.5.0 ZEO-5.2.1 ZODB-5.5.1 ZODB3-3.11.0 attrs-19.3.0 \ - beautifulsoup4-4.8.2 cffi-1.13.2 coverage-5.0.3 hupper-1.9.1 \ - importlib-metadata-1.4.0 more-itertools-8.1.0 packaging-20.0 \ - persistent-4.5.1 plaster-1.0 plaster-pastedeploy-0.7 pluggy-0.13.1 \ - py-1.8.1 pycparser-2.19 pyparsing-2.4.6 pyramid-1.10.4 \ - pyramid-chameleon-0.3 pyramid-debugtoolbar-4.5.2 pyramid-mako-1.1.0 \ - pyramid-retry-2.1 pyramid-tm-2.4 pyramid-zodbconn-0.8.1 pytest-5.3.2 \ - pytest-cov-2.8.1 repoze.lru-0.7 six-1.13.0 soupsieve-1.9.5 \ - transaction-3.0.0 translationstring-1.3 tutorial venusian-3.0.0 \ - waitress-1.4.2 wcwidth-0.1.8 webob-1.8.5 zc.lockfile-2.0 zdaemon-4.3 \ - zipp-0.6.0 zodbpickle-2.0.0 zodburi-2.4.0 zope.deprecation-4.4.0 \ - zope.interface-4.7.1 + Successfully installed BTrees-4.7.2 Chameleon-3.8.1 Mako-1.1.3 MarkupSafe-1.1.1 PasteDeploy-2.1.1 Pygments-2.7.3 WebTest-2.0.35 ZConfig-3.5.0 ZEO-5.2.2 ZODB-5.6.0 attrs-20.3.0 beautifulsoup4-4.9.3 cffi-1.14.4 coverage-5.3.1 hupper-1.10.2 iniconfig-1.1.1 packaging-20.8 persistent-4.6.4 plaster-1.0 plaster-pastedeploy-0.7 pluggy-0.13.1 py-1.10.0 pycparser-2.20 pyparsing-2.4.7 pyramid-1.10.5 pyramid-chameleon-0.3 pyramid-debugtoolbar-4.9 pyramid-mako-1.1.0 pyramid-retry-2.1.1 pyramid-tm-2.4 pyramid-zodbconn-0.8.1 pytest-6.2.1 pytest-cov-2.10.1 repoze.lru-0.7 six-1.15.0 soupsieve-2.1 toml-0.10.2 transaction-3.0.1 translationstring-1.4 tutorial venusian-3.0.0 waitress-1.4.4 webob-1.8.6 zc.lockfile-2.0 zdaemon-4.3 zodbpickle-2.0.0 zodburi-2.4.0 zope.deprecation-4.4.0 zope.interface-5.2.0 Testing requirements are defined in our project's ``setup.py`` file, in the ``tests_require`` and ``extras_require`` stanzas. @@ -276,9 +263,9 @@ If successful, you will see output something like this: .. code-block:: bash ======================== test session starts ========================= - platform darwin -- Python 3.7.3, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 + platform darwin -- Python 3.9.0, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 rootdir: /filepath/tutorial, inifile: pytest.ini, testpaths: tutorial - plugins: cov-2.8.1 + plugins: cov-2.10.1 collected 4 items tests/test_functional.py .. [ 50%] diff --git a/docs/tutorials/wiki/src/authorization/README.txt b/docs/tutorials/wiki/src/authorization/README.txt index 8a56d14af..b4a924cc4 100644 --- a/docs/tutorials/wiki/src/authorization/README.txt +++ b/docs/tutorials/wiki/src/authorization/README.txt @@ -4,15 +4,16 @@ myproj Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd tutorial -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/tutorials/wiki/src/authorization/pytest.ini b/docs/tutorials/wiki/src/authorization/pytest.ini index 42c3259f9..3df78fe9d 100644 --- a/docs/tutorials/wiki/src/authorization/pytest.ini +++ b/docs/tutorials/wiki/src/authorization/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = tutorial diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py index cdfa18e09..f619a9915 100644 --- a/docs/tutorials/wiki/src/authorization/setup.py +++ b/docs/tutorials/wiki/src/authorization/setup.py @@ -20,7 +20,7 @@ requires = [ 'pyramid_tm', 'pyramid_zodbconn', 'transaction', - 'ZODB3', + 'ZODB', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/authorization/tests/conftest.py b/docs/tutorials/wiki/src/authorization/tests/conftest.py index 6a702ae12..86793408d 100644 --- a/docs/tutorials/wiki/src/authorization/tests/conftest.py +++ b/docs/tutorials/wiki/src/authorization/tests/conftest.py @@ -1,7 +1,7 @@ import os from pyramid.paster import get_appsettings from pyramid.scripting import prepare -from pyramid.testing import DummyRequest +from pyramid.testing import DummyRequest, testConfig import pytest import transaction import webtest @@ -54,31 +54,37 @@ def app_request(app, tm): drawbacks in tests as it's harder to mock data and is heavier. """ - env = prepare(registry=app.registry) - request = env['request'] - request.host = 'example.com' - request.tm = tm - - yield request - env['closer']() + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request @pytest.fixture -def dummy_request(app, tm): +def dummy_request(tm): """ A lightweight dummy request. - This request is ultra-lightweight and should be used only when the - request itself is not a large focus in the call-stack. - - It is way easier to mock and control side-effects using this object. + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: - It does not have request extensions applied. - Threadlocals are not properly pushed. """ request = DummyRequest() - request.registry = app.registry request.host = 'example.com' request.tm = tm return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/tutorials/wiki/src/basiclayout/README.txt b/docs/tutorials/wiki/src/basiclayout/README.txt index 8a56d14af..b4a924cc4 100644 --- a/docs/tutorials/wiki/src/basiclayout/README.txt +++ b/docs/tutorials/wiki/src/basiclayout/README.txt @@ -4,15 +4,16 @@ myproj Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd tutorial -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/tutorials/wiki/src/basiclayout/pytest.ini b/docs/tutorials/wiki/src/basiclayout/pytest.ini index 42c3259f9..3df78fe9d 100644 --- a/docs/tutorials/wiki/src/basiclayout/pytest.ini +++ b/docs/tutorials/wiki/src/basiclayout/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = tutorial diff --git a/docs/tutorials/wiki/src/basiclayout/setup.py b/docs/tutorials/wiki/src/basiclayout/setup.py index f85780010..d70df0959 100644 --- a/docs/tutorials/wiki/src/basiclayout/setup.py +++ b/docs/tutorials/wiki/src/basiclayout/setup.py @@ -18,7 +18,7 @@ requires = [ 'pyramid_tm', 'pyramid_zodbconn', 'transaction', - 'ZODB3', + 'ZODB', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/basiclayout/tests/conftest.py b/docs/tutorials/wiki/src/basiclayout/tests/conftest.py index 6a702ae12..86793408d 100644 --- a/docs/tutorials/wiki/src/basiclayout/tests/conftest.py +++ b/docs/tutorials/wiki/src/basiclayout/tests/conftest.py @@ -1,7 +1,7 @@ import os from pyramid.paster import get_appsettings from pyramid.scripting import prepare -from pyramid.testing import DummyRequest +from pyramid.testing import DummyRequest, testConfig import pytest import transaction import webtest @@ -54,31 +54,37 @@ def app_request(app, tm): drawbacks in tests as it's harder to mock data and is heavier. """ - env = prepare(registry=app.registry) - request = env['request'] - request.host = 'example.com' - request.tm = tm - - yield request - env['closer']() + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request @pytest.fixture -def dummy_request(app, tm): +def dummy_request(tm): """ A lightweight dummy request. - This request is ultra-lightweight and should be used only when the - request itself is not a large focus in the call-stack. - - It is way easier to mock and control side-effects using this object. + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: - It does not have request extensions applied. - Threadlocals are not properly pushed. """ request = DummyRequest() - request.registry = app.registry request.host = 'example.com' request.tm = tm return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/tutorials/wiki/src/installation/README.txt b/docs/tutorials/wiki/src/installation/README.txt index 8a56d14af..b4a924cc4 100644 --- a/docs/tutorials/wiki/src/installation/README.txt +++ b/docs/tutorials/wiki/src/installation/README.txt @@ -4,15 +4,16 @@ myproj Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd tutorial -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/tutorials/wiki/src/installation/pytest.ini b/docs/tutorials/wiki/src/installation/pytest.ini index 42c3259f9..3df78fe9d 100644 --- a/docs/tutorials/wiki/src/installation/pytest.ini +++ b/docs/tutorials/wiki/src/installation/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = tutorial diff --git a/docs/tutorials/wiki/src/installation/setup.py b/docs/tutorials/wiki/src/installation/setup.py index f85780010..d70df0959 100644 --- a/docs/tutorials/wiki/src/installation/setup.py +++ b/docs/tutorials/wiki/src/installation/setup.py @@ -18,7 +18,7 @@ requires = [ 'pyramid_tm', 'pyramid_zodbconn', 'transaction', - 'ZODB3', + 'ZODB', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/installation/tests/conftest.py b/docs/tutorials/wiki/src/installation/tests/conftest.py index 6a702ae12..86793408d 100644 --- a/docs/tutorials/wiki/src/installation/tests/conftest.py +++ b/docs/tutorials/wiki/src/installation/tests/conftest.py @@ -1,7 +1,7 @@ import os from pyramid.paster import get_appsettings from pyramid.scripting import prepare -from pyramid.testing import DummyRequest +from pyramid.testing import DummyRequest, testConfig import pytest import transaction import webtest @@ -54,31 +54,37 @@ def app_request(app, tm): drawbacks in tests as it's harder to mock data and is heavier. """ - env = prepare(registry=app.registry) - request = env['request'] - request.host = 'example.com' - request.tm = tm - - yield request - env['closer']() + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request @pytest.fixture -def dummy_request(app, tm): +def dummy_request(tm): """ A lightweight dummy request. - This request is ultra-lightweight and should be used only when the - request itself is not a large focus in the call-stack. - - It is way easier to mock and control side-effects using this object. + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: - It does not have request extensions applied. - Threadlocals are not properly pushed. """ request = DummyRequest() - request.registry = app.registry request.host = 'example.com' request.tm = tm return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/tutorials/wiki/src/models/README.txt b/docs/tutorials/wiki/src/models/README.txt index 8a56d14af..b4a924cc4 100644 --- a/docs/tutorials/wiki/src/models/README.txt +++ b/docs/tutorials/wiki/src/models/README.txt @@ -4,15 +4,16 @@ myproj Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd tutorial -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/tutorials/wiki/src/models/pytest.ini b/docs/tutorials/wiki/src/models/pytest.ini index 42c3259f9..3df78fe9d 100644 --- a/docs/tutorials/wiki/src/models/pytest.ini +++ b/docs/tutorials/wiki/src/models/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = tutorial diff --git a/docs/tutorials/wiki/src/models/setup.py b/docs/tutorials/wiki/src/models/setup.py index f85780010..d70df0959 100644 --- a/docs/tutorials/wiki/src/models/setup.py +++ b/docs/tutorials/wiki/src/models/setup.py @@ -18,7 +18,7 @@ requires = [ 'pyramid_tm', 'pyramid_zodbconn', 'transaction', - 'ZODB3', + 'ZODB', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/models/tests/conftest.py b/docs/tutorials/wiki/src/models/tests/conftest.py index 6a702ae12..86793408d 100644 --- a/docs/tutorials/wiki/src/models/tests/conftest.py +++ b/docs/tutorials/wiki/src/models/tests/conftest.py @@ -1,7 +1,7 @@ import os from pyramid.paster import get_appsettings from pyramid.scripting import prepare -from pyramid.testing import DummyRequest +from pyramid.testing import DummyRequest, testConfig import pytest import transaction import webtest @@ -54,31 +54,37 @@ def app_request(app, tm): drawbacks in tests as it's harder to mock data and is heavier. """ - env = prepare(registry=app.registry) - request = env['request'] - request.host = 'example.com' - request.tm = tm - - yield request - env['closer']() + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request @pytest.fixture -def dummy_request(app, tm): +def dummy_request(tm): """ A lightweight dummy request. - This request is ultra-lightweight and should be used only when the - request itself is not a large focus in the call-stack. - - It is way easier to mock and control side-effects using this object. + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: - It does not have request extensions applied. - Threadlocals are not properly pushed. """ request = DummyRequest() - request.registry = app.registry request.host = 'example.com' request.tm = tm return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/tutorials/wiki/src/tests/README.txt b/docs/tutorials/wiki/src/tests/README.txt index 8a56d14af..b4a924cc4 100644 --- a/docs/tutorials/wiki/src/tests/README.txt +++ b/docs/tutorials/wiki/src/tests/README.txt @@ -4,15 +4,16 @@ myproj Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd tutorial -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/tutorials/wiki/src/tests/pytest.ini b/docs/tutorials/wiki/src/tests/pytest.ini index 42c3259f9..3df78fe9d 100644 --- a/docs/tutorials/wiki/src/tests/pytest.ini +++ b/docs/tutorials/wiki/src/tests/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = tutorial diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py index cdfa18e09..f619a9915 100644 --- a/docs/tutorials/wiki/src/tests/setup.py +++ b/docs/tutorials/wiki/src/tests/setup.py @@ -20,7 +20,7 @@ requires = [ 'pyramid_tm', 'pyramid_zodbconn', 'transaction', - 'ZODB3', + 'ZODB', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/views/README.txt b/docs/tutorials/wiki/src/views/README.txt index 8a56d14af..b4a924cc4 100644 --- a/docs/tutorials/wiki/src/views/README.txt +++ b/docs/tutorials/wiki/src/views/README.txt @@ -4,15 +4,16 @@ myproj Getting Started --------------- -- Change directory into your newly created project. +- Change directory into your newly created project if not already there. Your + current directory should be the same as this README.txt file and setup.py. cd tutorial -- Create a Python virtual environment. +- Create a Python virtual environment, if not already created. python3 -m venv env -- Upgrade packaging tools. +- Upgrade packaging tools, if necessary. env/bin/pip install --upgrade pip setuptools diff --git a/docs/tutorials/wiki/src/views/pytest.ini b/docs/tutorials/wiki/src/views/pytest.ini index 42c3259f9..3df78fe9d 100644 --- a/docs/tutorials/wiki/src/views/pytest.ini +++ b/docs/tutorials/wiki/src/views/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict +addopts = --strict-markers testpaths = tutorial diff --git a/docs/tutorials/wiki/src/views/setup.py b/docs/tutorials/wiki/src/views/setup.py index 86c778bf2..786691e21 100644 --- a/docs/tutorials/wiki/src/views/setup.py +++ b/docs/tutorials/wiki/src/views/setup.py @@ -19,7 +19,7 @@ requires = [ 'pyramid_tm', 'pyramid_zodbconn', 'transaction', - 'ZODB3', + 'ZODB', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/views/tests/conftest.py b/docs/tutorials/wiki/src/views/tests/conftest.py index 6a702ae12..86793408d 100644 --- a/docs/tutorials/wiki/src/views/tests/conftest.py +++ b/docs/tutorials/wiki/src/views/tests/conftest.py @@ -1,7 +1,7 @@ import os from pyramid.paster import get_appsettings from pyramid.scripting import prepare -from pyramid.testing import DummyRequest +from pyramid.testing import DummyRequest, testConfig import pytest import transaction import webtest @@ -54,31 +54,37 @@ def app_request(app, tm): drawbacks in tests as it's harder to mock data and is heavier. """ - env = prepare(registry=app.registry) - request = env['request'] - request.host = 'example.com' - request.tm = tm - - yield request - env['closer']() + with prepare(registry=app.registry) as env: + request = env['request'] + request.host = 'example.com' + yield request @pytest.fixture -def dummy_request(app, tm): +def dummy_request(tm): """ A lightweight dummy request. - This request is ultra-lightweight and should be used only when the - request itself is not a large focus in the call-stack. - - It is way easier to mock and control side-effects using this object. + This request is ultra-lightweight and should be used only when the request + itself is not a large focus in the call-stack. It is much easier to mock + and control side-effects using this object, however: - It does not have request extensions applied. - Threadlocals are not properly pushed. """ request = DummyRequest() - request.registry = app.registry request.host = 'example.com' request.tm = tm return request + +@pytest.fixture +def dummy_config(dummy_request): + """ + A dummy :class:`pyramid.config.Configurator` object. This allows for + mock configuration, including configuration for ``dummy_request``, as well + as pushing the appropriate threadlocals. + + """ + with testConfig(request=dummy_request) as config: + yield config diff --git a/docs/tutorials/wiki2/authentication.rst b/docs/tutorials/wiki2/authentication.rst index a798e7748..e8a770491 100644 --- a/docs/tutorials/wiki2/authentication.rst +++ b/docs/tutorials/wiki2/authentication.rst @@ -40,7 +40,7 @@ Update ``tutorial/security.py`` with the following content: :linenos: :language: python -Here we've defined a new security policy named ``MySecurityPolicy``, which is implementing most of the :class:`pyramid.interfaces.ISecurityPolicy` interface by tracking a :term:`identity` using a signed cookie implemented by :class:`pyramid.authentication.AuthTktCookieHelper` (lines 8-34). +Here we've defined a new security policy named ``MySecurityPolicy``, which is implementing most of the :class:`pyramid.interfaces.ISecurityPolicy` interface by tracking an :term:`identity` using a signed cookie implemented by :class:`pyramid.authentication.AuthTktCookieHelper` (lines 8-34). The security policy outputs the authenticated ``tutorial.models.User`` object for the logged-in user as the :term:`identity`, which is available as ``request.identity``. Our new :term:`security policy` defines how our application will remember, forget, and identify users. diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index 004f6ed31..c961d490b 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -338,15 +338,15 @@ If successful, you will see output something like this: .. code-block:: bash ======================== test session starts ======================== - platform -- Python 3.7.3, pytest-5.3.2, py-1.8.1, pluggy-0.13.1 + platform darwin -- Python 3.9.0, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 rootdir: <somepath>/tutorial, inifile: pytest.ini, testpaths: tutorial, tests - plugins: cov-2.8.1 + plugins: cov-2.10.1 collected 5 items - tests/test_functional.py .. - tests/test_views.py ... + tests/test_functional.py .. [ 40%] + tests/test_views.py ... [100%] - ---------- coverage: platform darwin, python 3.7.4-final-0 ----------- + ---------- coverage: platform darwin, python 3.9.0-final-0 ----------- Name Stmts Miss Cover Missing ---------------------------------------------------------------------------------- tutorial/__init__.py 8 0 100% @@ -360,10 +360,10 @@ If successful, you will see output something like this: tutorial/scripts/__init__.py 0 0 100% tutorial/scripts/initialize_db.py 22 14 36% 15-16, 20-25, 29-38 tutorial/views/__init__.py 0 0 100% - tutorial/views/default.py 12 0 100% - tutorial/views/notfound.py 4 0 100% + tutorial/views/default.py 13 0 100% + tutorial/views/notfound.py 5 0 100% ---------------------------------------------------------------------------------- - TOTAL 136 27 80% + TOTAL 138 27 80% ===================== 5 passed in 0.77 seconds ====================== |
