diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/designdefense.rst | 142 | ||||
| -rw-r--r-- | docs/narr/MyProject/development.ini | 6 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/templates/mytemplate.pt | 13 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/tests.py | 25 | ||||
| -rw-r--r-- | docs/narr/MyProject/production.ini | 2 | ||||
| -rw-r--r-- | docs/narr/MyProject/setup.py | 45 | ||||
| -rw-r--r-- | docs/narr/project.rst | 98 | ||||
| -rw-r--r-- | docs/narr/scaffolding.rst | 8 | ||||
| -rw-r--r-- | docs/narr/startup.rst | 27 | ||||
| -rw-r--r-- | docs/narr/testing.rst | 73 | ||||
| -rw-r--r-- | docs/narr/zca.rst | 257 |
11 files changed, 331 insertions, 365 deletions
diff --git a/docs/designdefense.rst b/docs/designdefense.rst index ee6d5a317..478289c2b 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -7,98 +7,94 @@ From time to time, challenges to various aspects of :app:`Pyramid` design are lodged. To give context to discussions that follow, we detail some of the design decisions and trade-offs here. In some cases, we acknowledge that the framework can be made better and we describe future steps which will be taken -to improve it; in some cases we just file the challenge as noted, as -obviously you can't please everyone all of the time. +to improve it. In others we just file the challenge as noted, as obviously you +can't please everyone all of the time. Pyramid Provides More Than One Way to Do It ------------------------------------------- A canon of Python popular culture is "TIOOWTDI" ("there is only one way to do -it", a slighting, tongue-in-cheek reference to Perl's "TIMTOWTDI", which is -an acronym for "there is more than one way to do it"). - -:app:`Pyramid` is, for better or worse, a "TIMTOWTDI" system. For example, -it includes more than one way to resolve a URL to a :term:`view callable`: -via :term:`url dispatch` or :term:`traversal`. Multiple methods of -configuration exist: :term:`imperative configuration`, :term:`configuration -decoration`, and :term:`ZCML` (optionally via :term:`pyramid_zcml`). It works -with multiple different kinds of persistence and templating systems. And so -on. However, the existence of most of these overlapping ways to do things -are not without reason and purpose: we have a number of audiences to serve, -and we believe that TIMTOWTI at the web framework level actually *prevents* a -much more insidious and harmful set of duplication at higher levels in the -Python web community. - -:app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of -people with many years of prior :term:`Zope` experience. The idea of +it", a slighting, tongue-in-cheek reference to Perl's "TIMTOWTDI", which is an +acronym for "there is more than one way to do it"). + +:app:`Pyramid` is, for better or worse, a "TIMTOWTDI" system. For example, it +includes more than one way to resolve a URL to a :term:`view callable`: via +:term:`url dispatch` or :term:`traversal`. Multiple methods of configuration +exist: :term:`imperative configuration`, :term:`configuration decoration`, and +:term:`ZCML` (optionally via :term:`pyramid_zcml`). It works with multiple +different kinds of persistence and templating systems. And so on. However, the +existence of most of these overlapping ways to do things are not without reason +and purpose: we have a number of audiences to serve, and we believe that +TIMTOWTDI at the web framework level actually *prevents* a much more insidious +and harmful set of duplication at higher levels in the Python web community. + +:app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of people +with many years of prior :term:`Zope` experience. The idea of :term:`traversal` and the way :term:`view lookup` works was stolen entirely from Zope. The authorization subsystem provided by :app:`Pyramid` is a derivative of Zope's. The idea that an application can be *extended* without forking is also a Zope derivative. Implementations of these features were *required* to allow the :app:`Pyramid` -authors to build the bread-and-butter CMS-type systems for customers in the -way in which they were accustomed. No other system, save for Zope itself, -had such features, and Zope itself was beginning to show signs of its age. -We were becoming hampered by consequences of its early design mistakes. -Zope's lack of documentation was also difficult to work around: it was hard -to hire smart people to work on Zope applications, because there was no -comprehensive documentation set to point them at which explained "it all" in -one consumable place, and it was too large and self-inconsistent to document -properly. Before :mod:`repoze.bfg` went under development, its authors -obviously looked around for other frameworks that fit the bill. But no -non-Zope framework did. So we embarked on building :mod:`repoze.bfg`. +authors to build the bread-and-butter CMS-type systems for customers in the way +in which they were accustomed. No other system, save for Zope itself, had such +features, and Zope itself was beginning to show signs of its age. We were +becoming hampered by consequences of its early design mistakes. Zope's lack of +documentation was also difficult to work around. It was hard to hire smart +people to work on Zope applications because there was no comprehensive +documentation set which explained "it all" in one consumable place, and it was +too large and self-inconsistent to document properly. Before :mod:`repoze.bfg` +went under development, its authors obviously looked around for other +frameworks that fit the bill. But no non-Zope framework did. So we embarked on +building :mod:`repoze.bfg`. As the result of our research, however, it became apparent that, despite the -fact that no *one* framework had all the features we required, lots of -existing frameworks had good, and sometimes very compelling ideas. In -particular, :term:`URL dispatch` is a more direct mechanism to map URLs to -code. +fact that no *one* framework had all the features we required, lots of existing +frameworks had good, and sometimes very compelling ideas. In particular, +:term:`URL dispatch` is a more direct mechanism to map URLs to code. So, although we couldn't find a framework, save for Zope, that fit our needs, and while we incorporated a lot of Zope ideas into BFG, we also emulated the features we found compelling in other frameworks (such as :term:`url -dispatch`). After the initial public release of BFG, as time went on, -features were added to support people allergic to various Zope-isms in the -system, such as the ability to configure the application using -:term:`imperative configuration` and :term:`configuration decoration` rather -than solely using :term:`ZCML`, and the elimination of the required use of -:term:`interface` objects. It soon became clear that we had a system that -was very generic, and was beginning to appeal to non-Zope users as well as -ex-Zope users. +dispatch`). After the initial public release of BFG, as time went on, features +were added to support people allergic to various Zope-isms in the system, such +as the ability to configure the application using :term:`imperative +configuration` and :term:`configuration decoration`, rather than solely using +:term:`ZCML`, and the elimination of the required use of :term:`interface` +objects. It soon became clear that we had a system that was very generic, and +was beginning to appeal to non-Zope users as well as ex-Zope users. As the result of this generalization, it became obvious BFG shared 90% of its -featureset with the featureset of Pylons 1, and thus had a very similar -target market. Because they were so similar, choosing between the two -systems was an exercise in frustration for an otherwise non-partisan -developer. It was also strange for the Pylons and BFG development -communities to be in competition for the same set of users, given how similar -the two frameworks were. So the Pylons and BFG teams began to work together -to form a plan to merge. The features missing from BFG (notably :term:`view -handler` classes, flash messaging, and other minor missing bits), were added, -to provide familiarity to ex-Pylons users. The result is :app:`Pyramid`. - -The Python web framework space is currently notoriously balkanized. We're -truly hoping that the amalgamation of components in :app:`Pyramid` will -appeal to at least two currently very distinct sets of users: Pylons and BFG -users. By unifying the best concepts from Pylons and BFG into a single -codebase and leaving the bad concepts from their ancestors behind, we'll be -able to consolidate our efforts better, share more code, and promote our -efforts as a unit rather than competing pointlessly. We hope to be able to -shortcut the pack mentality which results in a *much larger* duplication of -effort, represented by competing but incredibly similar applications and -libraries, each built upon a specific low level stack that is incompatible -with the other. We'll also shrink the choice of credible Python web -frameworks down by at least one. We're also hoping to attract users from -other communities (such as Zope's and TurboGears') by providing the features -they require, while allowing enough flexibility to do things in a familiar -fashion. Some overlap of functionality to achieve these goals is expected -and unavoidable, at least if we aim to prevent pointless duplication at -higher levels. If we've done our job well enough, the various audiences will -be able to coexist and cooperate rather than firing at each other across some -imaginary web framework DMZ. - -Pyramid Uses A Zope Component Architecture ("ZCA") Registry +feature set with the feature set of Pylons 1, and thus had a very similar +target market. Because they were so similar, choosing between the two systems +was an exercise in frustration for an otherwise non-partisan developer. It was +also strange for the Pylons and BFG development communities to be in +competition for the same set of users, given how similar the two frameworks +were. So the Pylons and BFG teams began to work together to form a plan to +merge. The features missing from BFG (notably :term:`view handler` classes, +flash messaging, and other minor missing bits), were added to provide +familiarity to ex-Pylons users. The result is :app:`Pyramid`. + +The Python web framework space is currently notoriously balkanized. We're truly +hoping that the amalgamation of components in :app:`Pyramid` will appeal to at +least two currently very distinct sets of users: Pylons and BFG users. By +unifying the best concepts from Pylons and BFG into a single codebase, and +leaving the bad concepts from their ancestors behind, we'll be able to +consolidate our efforts better, share more code, and promote our efforts as a +unit rather than competing pointlessly. We hope to be able to shortcut the pack +mentality which results in a *much larger* duplication of effort, represented +by competing but incredibly similar applications and libraries, each built upon +a specific low level stack that is incompatible with the other. We'll also +shrink the choice of credible Python web frameworks down by at least one. We're +also hoping to attract users from other communities (such as Zope's and +TurboGears') by providing the features they require, while allowing enough +flexibility to do things in a familiar fashion. Some overlap of functionality +to achieve these goals is expected and unavoidable, at least if we aim to +prevent pointless duplication at higher levels. If we've done our job well +enough, the various audiences will be able to coexist and cooperate rather than +firing at each other across some imaginary web framework DMZ. + +Pyramid Uses a Zope Component Architecture ("ZCA") Registry ----------------------------------------------------------- :app:`Pyramid` uses a :term:`Zope Component Architecture` (ZCA) "component diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini index a9a26e56b..749e574eb 100644 --- a/docs/narr/MyProject/development.ini +++ b/docs/narr/MyProject/development.ini @@ -11,7 +11,7 @@ pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en -pyramid.includes = +pyramid.includes = pyramid_debugtoolbar # By default, the toolbar only appears for clients from IP addresses @@ -24,7 +24,7 @@ pyramid.includes = [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -57,4 +57,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt index e6b00a145..65d7f0609 100644 --- a/docs/narr/MyProject/myproject/templates/mytemplate.pt +++ b/docs/narr/MyProject/myproject/templates/mytemplate.pt @@ -8,12 +8,12 @@ <meta name="author" content="Pylons Project"> <link rel="shortcut icon" href="${request.static_url('myproject:static/pyramid-16x16.png')}"> - <title>Starter Template for The Pyramid Web Framework</title> + <title>Starter Scaffold for The Pyramid Web Framework</title> <!-- Bootstrap core CSS --> <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"> - <!-- Custom styles for this template --> + <!-- Custom styles for this scaffold --> <link href="${request.static_url('myproject:static/theme.css')}" rel="stylesheet"> <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> @@ -33,19 +33,20 @@ </div> <div class="col-md-10"> <div class="content"> - <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">starter template</span></h1> - <p class="lead">Welcome to <span class="font-normal">${project}</span>, an application generated by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p> + <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter scaffold</span></h1> + <p class="lead">Welcome to <span class="font-normal">${project}</span>, an application generated by<br>the <span class="font-normal">Pyramid Web Framework 1.6b2</span>.</p> </div> </div> </div> <div class="row"> <div class="links"> <ul> - <li class="current-version">Currently v1.5</li> - <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org">Docs</a></li> + <li class="current-version">Generated by v1.6b2</li> + <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/">Docs</a></li> <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li> <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li> <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li> + </ul> </div> </div> <div class="row"> diff --git a/docs/narr/MyProject/myproject/tests.py b/docs/narr/MyProject/myproject/tests.py index 8c60407e5..37df08a2a 100644 --- a/docs/narr/MyProject/myproject/tests.py +++ b/docs/narr/MyProject/myproject/tests.py @@ -16,31 +16,6 @@ class ViewTests(unittest.TestCase): 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): diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini index 9eae9e03f..3ccbe6619 100644 --- a/docs/narr/MyProject/production.ini +++ b/docs/narr/MyProject/production.ini @@ -51,4 +51,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py index 9f34540a7..8c019af51 100644 --- a/docs/narr/MyProject/setup.py +++ b/docs/narr/MyProject/setup.py @@ -1,42 +1,30 @@ -"""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 fp: - README = fp.read() - - -with open(os.path.join(HERE, 'CHANGES.txt')) as fp: - CHANGES = fp.read() +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 = [ +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='', @@ -44,10 +32,11 @@ setup(name='MyProject', packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=REQUIRES, - tests_require=TESTS_REQUIRE, - test_suite='myproject', + install_requires=requires, + tests_require=requires, + test_suite="myproject", entry_points="""\ [paste.app_factory] main = myproject:main - """) + """, + ) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 25f3931e9..4785b60c4 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -77,7 +77,7 @@ The below example uses the ``pcreate`` command to create a project with the On UNIX: -.. code-block:: text +.. code-block:: bash $ $VENV/bin/pcreate -s starter MyProject @@ -90,7 +90,7 @@ Or on Windows: Here's sample output from a run of ``pcreate`` on UNIX for a project we name ``MyProject``: -.. code-block:: text +.. code-block:: bash $ $VENV/bin/pcreate -s starter MyProject Creating template pyramid @@ -158,7 +158,7 @@ created project directory. On UNIX: -.. code-block:: text +.. code-block:: bash $ cd MyProject $ $VENV/bin/python setup.py develop @@ -172,7 +172,7 @@ Or on Windows: Elided output from a run of this command on UNIX is shown below: -.. code-block:: text +.. code-block:: bash $ cd MyProject $ $VENV/bin/python setup.py develop @@ -198,7 +198,7 @@ directory of your virtualenv). On UNIX: -.. code-block:: text +.. code-block:: bash $ $VENV/bin/python setup.py test -q @@ -210,7 +210,7 @@ Or on Windows: Here's sample output from a test run on UNIX: -.. code-block:: text +.. code-block:: bash $ $VENV/bin/python setup.py test -q running test @@ -221,11 +221,23 @@ Here's sample output from a test run on UNIX: writing dependency_links to MyProject.egg-info/dependency_links.txt writing entry points to MyProject.egg-info/entry_points.txt reading manifest file 'MyProject.egg-info/SOURCES.txt' + reading manifest template 'MANIFEST.in' + warning: no files found matching '*.cfg' + warning: no files found matching '*.rst' + warning: no files found matching '*.ico' under directory 'myproject' + warning: no files found matching '*.gif' under directory 'myproject' + warning: no files found matching '*.jpg' under directory 'myproject' + warning: no files found matching '*.txt' under directory 'myproject' + warning: no files found matching '*.mak' under directory 'myproject' + warning: no files found matching '*.mako' under directory 'myproject' + warning: no files found matching '*.js' under directory 'myproject' + warning: no files found matching '*.html' under directory 'myproject' + warning: no files found matching '*.xml' under directory 'myproject' writing manifest file 'MyProject.egg-info/SOURCES.txt' running build_ext - .. + . ---------------------------------------------------------------------- - Ran 1 test in 0.108s + Ran 1 test in 0.008s OK @@ -256,7 +268,7 @@ file. In our case, this file is named ``development.ini``. On UNIX: -.. code-block:: text +.. code-block:: bash $ $VENV/bin/pserve development.ini @@ -268,38 +280,38 @@ On Windows: Here's sample output from a run of ``pserve`` on UNIX: -.. code-block:: text +.. code-block:: bash $ $VENV/bin/pserve development.ini - Starting server in PID 16601. - serving on http://0.0.0.0:6543 - -When you use ``pserve`` to start the application implied by the default -rendering of a scaffold, it will respond to requests on *all* IP addresses -possessed by your system, not just requests to ``localhost``. This is what the -``0.0.0.0`` in ``serving on http://0.0.0.0:6543`` means. The server will -respond to requests made to ``127.0.0.1`` and on any external IP address. For -example, your system might be configured to have an external IP address -``192.168.1.50``. If that's the case, if you use a browser running on the same -system as Pyramid, it will be able to access the application via -``http://127.0.0.1:6543/`` as well as via ``http://192.168.1.50:6543/``. -However, *other people* on other computers on the same network will also be -able to visit your Pyramid application in their browser by visiting -``http://192.168.1.50:6543/``. - -If you want to restrict access such that only a browser running on the same -machine as Pyramid will be able to access your Pyramid application, edit the + Starting server in PID 16208. + serving on http://127.0.0.1:6543 + +Access is restricted such that only a browser running on the same machine as +Pyramid will be able to access your Pyramid application. However, if you want +to open access to other machines on the same network, then edit the ``development.ini`` file, and replace the ``host`` value in the -``[server:main]`` section. Change it from ``0.0.0.0`` to ``127.0.0.1``. For +``[server:main]`` section, changing it from ``127.0.0.1`` to ``0.0.0.0``. For example: .. code-block:: ini [server:main] use = egg:waitress#main - host = 127.0.0.1 + host = 0.0.0.0 port = 6543 +Now when you use ``pserve`` to start the application, it will respond to +requests on *all* IP addresses possessed by your system, not just requests to +``localhost``. This is what the ``0.0.0.0`` in +``serving on http://0.0.0.0:6543`` means. The server will respond to requests +made to ``127.0.0.1`` and on any external IP address. For example, your system +might be configured to have an external IP address ``192.168.1.50``. If that's +the case, if you use a browser running on the same system as Pyramid, it will +be able to access the application via ``http://127.0.0.1:6543/`` as well as via +``http://192.168.1.50:6543/``. However, *other people* on other computers on +the same network will also be able to visit your Pyramid application in their +browser by visiting ``http://192.168.1.50:6543/``. + You can change the port on which the server runs on by changing the same portion of the ``development.ini`` file. For example, you can change the ``port = 6543`` line in the ``development.ini`` file's ``[server:main]`` @@ -347,7 +359,7 @@ For example, on UNIX: $ $VENV/bin/pserve development.ini --reload Starting subprocess with file monitor Starting server in PID 16601. - serving on http://0.0.0.0:6543 + serving on http://127.0.0.1:6543 Now if you make a change to any of your project's ``.py`` files or ``.ini`` files, you'll see the server restart automatically: @@ -357,7 +369,7 @@ files, you'll see the server restart automatically: development.ini changed; reloading... -------------------- Restarting -------------------- Starting server in PID 16602. - serving on http://0.0.0.0:6543 + serving on http://127.0.0.1:6543 Changes to template files (such as ``.pt`` or ``.mak`` files) won't cause the server to restart. Changes to template files don't require a server restart as @@ -579,18 +591,16 @@ file. The name ``main`` is a convention used by PasteDeploy signifying that it is the default application. The ``[server:main]`` section of the configuration file configures a WSGI -server which listens on TCP port 6543. It is configured to listen on all -interfaces (``0.0.0.0``). This means that any remote system which has TCP -access to your system can see your Pyramid application. +server which listens on TCP port 6543. It is configured to listen on localhost +only (``127.0.0.1``). .. _MyProject_ini_logging: -The sections that live between the markers ``# Begin logging configuration`` -and ``# End logging configuration`` represent Python's standard library -:mod:`logging` module configuration for your application. The sections between -these two markers are passed to the `logging module's config file configuration -engine <http://docs.python.org/howto/logging.html#configuring-logging>`_ when -the ``pserve`` or ``pshell`` commands are executed. The default configuration +The sections after ``# logging configuration`` represent Python's standard +library :mod:`logging` module configuration for your application. These +sections are passed to the `logging module's config file configuration engine +<http://docs.python.org/howto/logging.html#configuring-logging>`_ when the +``pserve`` or ``pshell`` commands are executed. The default configuration sends application logging output to the standard error output of your terminal. For more information about logging configuration, see :ref:`logging_chapter`. @@ -680,8 +690,8 @@ testing, packaging, and distributing your application. ``setup.py`` is the de facto standard which Python developers use to distribute their reusable code. You can read more about ``setup.py`` files and their usage in the `Setuptools documentation - <http://peak.telecommunity.com/DevCenter/setuptools>`_ and `The Hitchhiker's - Guide to Packaging <http://guide.python-distribute.org/>`_. + <http://peak.telecommunity.com/DevCenter/setuptools>`_ and `Python Packaging + User Guide <https://packaging.python.org/en/latest/>`_. Our generated ``setup.py`` looks like this: @@ -912,7 +922,7 @@ The ``tests.py`` module includes unit tests for your application. .. literalinclude:: MyProject/myproject/tests.py :language: python - :lines: 1-18 + :lines: 1-17 :linenos: This sample ``tests.py`` file has a single unit test defined within it. This diff --git a/docs/narr/scaffolding.rst b/docs/narr/scaffolding.rst index 8677359de..164ceb3bf 100644 --- a/docs/narr/scaffolding.rst +++ b/docs/narr/scaffolding.rst @@ -22,10 +22,10 @@ found by the ``pcreate`` command. To create a scaffold template, create a Python :term:`distribution` to house the scaffold which includes a ``setup.py`` that relies on the ``setuptools`` -package. See `Creating a Package -<http://guide.python-distribute.org/creation.html>`_ for more information about -how to do this. For example, we'll pretend the distribution you create is -named ``CoolExtension``, and it has a package directory within it named +package. See `Packaging and Distributing Projects +<https://packaging.python.org/en/latest/distributing/>`_ for more information +about how to do this. For example, we'll pretend the distribution you create +is named ``CoolExtension``, and it has a package directory within it named ``coolextension``. Once you've created the distribution, put a "scaffolds" directory within your diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index 485f6b181..3e168eaea 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -6,15 +6,15 @@ Startup When you cause a :app:`Pyramid` application to start up in a console window, you'll see something much like this show up on the console: -.. code-block:: text +.. code-block:: bash - $ pserve development.ini - Starting server in PID 16601. - serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 + $ $VENV/bin/pserve development.ini + Starting server in PID 16305. + serving on http://127.0.0.1:6543 This chapter explains what happens between the time you press the "Return" key on your keyboard after typing ``pserve development.ini`` and the time the line -``serving on 0.0.0.0:6543 ...`` is output to your console. +``serving on http://127.0.0.1:6543`` is output to your console. .. index:: single: startup process @@ -92,11 +92,11 @@ Here's a high-level time-ordered overview of what happens when you press In this case, the ``myproject.__init__:main`` function referred to by the entry point URI ``egg:MyProject`` (see :ref:`MyProject_ini` for more information about entry point URIs, and how they relate to callables) will - receive the key/value pairs ``{'pyramid.reload_templates':'true', - 'pyramid.debug_authorization':'false', 'pyramid.debug_notfound':'false', - 'pyramid.debug_routematch':'false', 'pyramid.debug_templates':'true', - 'pyramid.default_locale_name':'en'}``. See :ref:`environment_chapter` for - the meanings of these keys. + receive the key/value pairs ``{pyramid.reload_templates = true, + pyramid.debug_authorization = false, pyramid.debug_notfound = false, + pyramid.debug_routematch = false, pyramid.default_locale_name = en, and + pyramid.includes = pyramid_debugtoolbar}``. See :ref:`environment_chapter` + for the meanings of these keys. #. The ``main`` function first constructs a :class:`~pyramid.config.Configurator` instance, passing the ``settings`` @@ -131,10 +131,9 @@ Here's a high-level time-ordered overview of what happens when you press #. ``pserve`` starts the WSGI *server* defined within the ``[server:main]`` section. In our case, this is the Waitress server (``use = egg:waitress#main``), and it will listen on all interfaces (``host = - 0.0.0.0``), on port number 6543 (``port = 6543``). The server code itself - is what prints ``serving on 0.0.0.0:6543 view at http://127.0.0.1:6543``. - The server serves the application, and the application is running, waiting - to receive requests. + 127.0.0.1``), on port number 6543 (``port = 6543``). The server code itself + is what prints ``serving on http://127.0.0.1:6543``. The server serves the + application, and the application is running, waiting to receive requests. .. seealso:: Logging configuration is described in the :ref:`logging_chapter` chapter. diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index c05ee41ad..a3f62058b 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -348,26 +348,6 @@ code's integration with the rest of :app:`Pyramid`. See also :ref:`including_configuration` -Let's demonstrate this by showing an integration test for a view. - -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``: - - .. literalinclude:: MyProject/myproject/views.py - :linenos: - :lines: 1-6 - :language: python - -You'd then create a ``tests`` module within your ``myproject`` package, -containing the following test code: - - .. 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 @@ -388,22 +368,53 @@ package, which provides APIs for invoking HTTP(S) requests to your application. Regardless of which testing :term:`package` you use, ensure to add a ``tests_require`` dependency on that package to your application's -``setup.py`` file: +``setup.py`` file. Using the project ``MyProject`` generated by the starter +scaffold as described in :doc:`project`, we would insert the following code immediately following the +``requires`` block in the file ``MyProject/setup.py``. - .. literalinclude:: MyProject/setup.py - :linenos: - :emphasize-lines: 26-28,48 - :language: python +.. code-block:: ini + :linenos: + :lineno-start: 11 + :emphasize-lines: 8- + + requires = [ + 'pyramid', + 'pyramid_chameleon', + 'pyramid_debugtoolbar', + 'waitress', + ] + + test_requires = [ + 'webtest', + ] + +Remember to change the dependency. + +.. code-block:: ini + :linenos: + :lineno-start: 39 + :emphasize-lines: 2 + + install_requires=requires, + tests_require=test_requires, + test_suite="myproject", + +As always, whenever you change your dependencies, make sure to run the +following command. + +.. code-block:: bash + + $VENV/bin/python setup.py develop -Let us assume 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: +In your ``MyPackage`` project, your :term:`package` is named ``myproject`` +which contains a ``views`` module, which in turn contains a :term:`view` +function ``my_view`` that returns an HTML body when the root URL is invoked: .. literalinclude:: MyProject/myproject/views.py :linenos: :language: python -Then the following example functional test demonstrates invoking the above +The following example functional test demonstrates invoking the above :term:`view`: .. literalinclude:: MyProject/myproject/tests.py @@ -414,9 +425,9 @@ Then the following example functional test demonstrates invoking the above 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`` +``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``. +returned HTML contains the text ``Pyramid``. See the :term:`WebTest` documentation for further information about the methods available to a :class:`webtest.app.TestApp` instance. diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst index b0e9b1709..784886563 100644 --- a/docs/narr/zca.rst +++ b/docs/narr/zca.rst @@ -9,17 +9,16 @@ .. _zca_chapter: Using the Zope Component Architecture in :app:`Pyramid` -========================================================== +======================================================= -Under the hood, :app:`Pyramid` uses a :term:`Zope Component -Architecture` component registry as its :term:`application registry`. -The Zope Component Architecture is referred to colloquially as the -"ZCA." +Under the hood, :app:`Pyramid` uses a :term:`Zope Component Architecture` +component registry as its :term:`application registry`. The Zope Component +Architecture is referred to colloquially as the "ZCA." The ``zope.component`` API used to access data in a traditional Zope -application can be opaque. For example, here is a typical "unnamed -utility" lookup using the :func:`zope.component.getUtility` global API -as it might appear in a traditional Zope application: +application can be opaque. For example, here is a typical "unnamed utility" +lookup using the :func:`zope.component.getUtility` global API as it might +appear in a traditional Zope application: .. code-block:: python :linenos: @@ -28,23 +27,21 @@ as it might appear in a traditional Zope application: from zope.component import getUtility settings = getUtility(ISettings) -After this code runs, ``settings`` will be a Python dictionary. But -it's unlikely that any "civilian" will be able to figure this out just -by reading the code casually. When the ``zope.component.getUtility`` -API is used by a developer, the conceptual load on a casual reader of -code is high. +After this code runs, ``settings`` will be a Python dictionary. But it's +unlikely that any "civilian" will be able to figure this out just by reading +the code casually. When the ``zope.component.getUtility`` API is used by a +developer, the conceptual load on a casual reader of code is high. -While the ZCA is an excellent tool with which to build a *framework* -such as :app:`Pyramid`, it is not always the best tool with which -to build an *application* due to the opacity of the ``zope.component`` -APIs. Accordingly, :app:`Pyramid` tends to hide the presence of the -ZCA from application developers. You needn't understand the ZCA to -create a :app:`Pyramid` application; its use is effectively only a -framework implementation detail. +While the ZCA is an excellent tool with which to build a *framework* such as +:app:`Pyramid`, it is not always the best tool with which to build an +*application* due to the opacity of the ``zope.component`` APIs. Accordingly, +:app:`Pyramid` tends to hide the presence of the ZCA from application +developers. You needn't understand the ZCA to create a :app:`Pyramid` +application; its use is effectively only a framework implementation detail. -However, developers who are already used to writing :term:`Zope` -applications often still wish to use the ZCA while building a -:app:`Pyramid` application; :app:`Pyramid` makes this possible. +However, developers who are already used to writing :term:`Zope` applications +often still wish to use the ZCA while building a :app:`Pyramid` application. +:app:`Pyramid` makes this possible. .. index:: single: get_current_registry @@ -52,87 +49,81 @@ applications often still wish to use the ZCA while building a single: getSiteManager single: ZCA global API -Using the ZCA Global API in a :app:`Pyramid` Application ------------------------------------------------------------ - -:term:`Zope` uses a single ZCA registry -- the "global" ZCA registry --- for all Zope applications that run in the same Python process, -effectively making it impossible to run more than one Zope application -in a single process. - -However, for ease of deployment, it's often useful to be able to run more -than a single application per process. For example, use of a -:term:`PasteDeploy` "composite" allows you to run separate individual WSGI -applications in the same process, each answering requests for some URL -prefix. This makes it possible to run, for example, a TurboGears application -at ``/turbogears`` and a :app:`Pyramid` application at ``/pyramid``, both -served up using the same :term:`WSGI` server within a single Python process. - -Most production Zope applications are relatively large, making it -impractical due to memory constraints to run more than one Zope -application per Python process. However, a :app:`Pyramid` application -may be very small and consume very little memory, so it's a reasonable -goal to be able to run more than one :app:`Pyramid` application per -process. - -In order to make it possible to run more than one :app:`Pyramid` -application in a single process, :app:`Pyramid` defaults to using a -separate ZCA registry *per application*. - -While this services a reasonable goal, it causes some issues when -trying to use patterns which you might use to build a typical -:term:`Zope` application to build a :app:`Pyramid` application. -Without special help, ZCA "global" APIs such as -:func:`zope.component.getUtility` and :func:`zope.component.getSiteManager` -will use the ZCA "global" registry. Therefore, these APIs -will appear to fail when used in a :app:`Pyramid` application, -because they'll be consulting the ZCA global registry rather than the -component registry associated with your :app:`Pyramid` application. - -There are three ways to fix this: by disusing the ZCA global API -entirely, by using -:meth:`pyramid.config.Configurator.hook_zca` or by passing -the ZCA global registry to the :term:`Configurator` constructor at -startup time. We'll describe all three methods in this section. +Using the ZCA global API in a :app:`Pyramid` application +-------------------------------------------------------- + +:term:`Zope` uses a single ZCA registry—the "global" ZCA registry—for all Zope +applications that run in the same Python process, effectively making it +impossible to run more than one Zope application in a single process. + +However, for ease of deployment, it's often useful to be able to run more than +a single application per process. For example, use of a :term:`PasteDeploy` +"composite" allows you to run separate individual WSGI applications in the same +process, each answering requests for some URL prefix. This makes it possible +to run, for example, a TurboGears application at ``/turbogears`` and a +:app:`Pyramid` application at ``/pyramid``, both served up using the same +:term:`WSGI` server within a single Python process. + +Most production Zope applications are relatively large, making it impractical +due to memory constraints to run more than one Zope application per Python +process. However, a :app:`Pyramid` application may be very small and consume +very little memory, so it's a reasonable goal to be able to run more than one +:app:`Pyramid` application per process. + +In order to make it possible to run more than one :app:`Pyramid` application in +a single process, :app:`Pyramid` defaults to using a separate ZCA registry *per +application*. + +While this services a reasonable goal, it causes some issues when trying to use +patterns which you might use to build a typical :term:`Zope` application to +build a :app:`Pyramid` application. Without special help, ZCA "global" APIs +such as :func:`zope.component.getUtility` and +:func:`zope.component.getSiteManager` will use the ZCA "global" registry. +Therefore, these APIs will appear to fail when used in a :app:`Pyramid` +application, because they'll be consulting the ZCA global registry rather than +the component registry associated with your :app:`Pyramid` application. + +There are three ways to fix this: by disusing the ZCA global API entirely, by +using :meth:`pyramid.config.Configurator.hook_zca` or by passing the ZCA global +registry to the :term:`Configurator` constructor at startup time. We'll +describe all three methods in this section. .. index:: single: request.registry .. _disusing_the_global_zca_api: -Disusing the Global ZCA API +Disusing the global ZCA API +++++++++++++++++++++++++++ ZCA "global" API functions such as ``zope.component.getSiteManager``, ``zope.component.getUtility``, :func:`zope.component.getAdapter`, and :func:`zope.component.getMultiAdapter` aren't strictly necessary. Every -component registry has a method API that offers the same -functionality; it can be used instead. For example, presuming the -``registry`` value below is a Zope Component Architecture component -registry, the following bit of code is equivalent to -``zope.component.getUtility(IFoo)``: +component registry has a method API that offers the same functionality; it can +be used instead. For example, presuming the ``registry`` value below is a Zope +Component Architecture component registry, the following bit of code is +equivalent to ``zope.component.getUtility(IFoo)``: .. code-block:: python registry.getUtility(IFoo) -The full method API is documented in the ``zope.component`` package, -but it largely mirrors the "global" API almost exactly. +The full method API is documented in the ``zope.component`` package, but it +largely mirrors the "global" API almost exactly. -If you are willing to disuse the "global" ZCA APIs and use the method -interface of a registry instead, you need only know how to obtain the -:app:`Pyramid` component registry. +If you are willing to disuse the "global" ZCA APIs and use the method interface +of a registry instead, you need only know how to obtain the :app:`Pyramid` +component registry. There are two ways of doing so: -- use the :func:`pyramid.threadlocal.get_current_registry` - function within :app:`Pyramid` view or resource code. This will - always return the "current" :app:`Pyramid` application registry. +- use the :func:`pyramid.threadlocal.get_current_registry` function within + :app:`Pyramid` view or resource code. This will always return the "current" + :app:`Pyramid` application registry. -- use the attribute of the :term:`request` object named ``registry`` - in your :app:`Pyramid` view code, eg. ``request.registry``. This - is the ZCA component registry related to the running - :app:`Pyramid` application. +- use the attribute of the :term:`request` object named ``registry`` in your + :app:`Pyramid` view code, e.g., ``request.registry``. This is the ZCA + component registry related to the running :app:`Pyramid` application. See :ref:`threadlocals_chapter` for more information about :func:`pyramid.threadlocal.get_current_registry`. @@ -142,7 +133,7 @@ See :ref:`threadlocals_chapter` for more information about .. _hook_zca: -Enabling the ZCA Global API by Using ``hook_zca`` +Enabling the ZCA global API by using ``hook_zca`` +++++++++++++++++++++++++++++++++++++++++++++++++ Consider the following bit of idiomatic :app:`Pyramid` startup code: @@ -157,34 +148,31 @@ Consider the following bit of idiomatic :app:`Pyramid` startup code: config.include('some.other.package') return config.make_wsgi_app() -When the ``app`` function above is run, a :term:`Configurator` is -constructed. When the configurator is created, it creates a *new* -:term:`application registry` (a ZCA component registry). A new -registry is constructed whenever the ``registry`` argument is omitted -when a :term:`Configurator` constructor is called, or when a -``registry`` argument with a value of ``None`` is passed to a -:term:`Configurator` constructor. - -During a request, the application registry created by the Configurator -is "made current". This means calls to -:func:`~pyramid.threadlocal.get_current_registry` in the thread -handling the request will return the component registry associated -with the application. - -As a result, application developers can use ``get_current_registry`` -to get the registry and thus get access to utilities and such, as per -:ref:`disusing_the_global_zca_api`. But they still cannot use the -global ZCA API. Without special treatment, the ZCA global APIs will -always return the global ZCA registry (the one in -``zope.component.globalregistry.base``). - -To "fix" this and make the ZCA global APIs use the "current" -:app:`Pyramid` registry, you need to call -:meth:`~pyramid.config.Configurator.hook_zca` within your setup code. -For example: +When the ``app`` function above is run, a :term:`Configurator` is constructed. +When the configurator is created, it creates a *new* :term:`application +registry` (a ZCA component registry). A new registry is constructed whenever +the ``registry`` argument is omitted, when a :term:`Configurator` constructor +is called, or when a ``registry`` argument with a value of ``None`` is passed +to a :term:`Configurator` constructor. + +During a request, the application registry created by the Configurator is "made +current". This means calls to +:func:`~pyramid.threadlocal.get_current_registry` in the thread handling the +request will return the component registry associated with the application. + +As a result, application developers can use ``get_current_registry`` to get the +registry and thus get access to utilities and such, as per +:ref:`disusing_the_global_zca_api`. But they still cannot use the global ZCA +API. Without special treatment, the ZCA global APIs will always return the +global ZCA registry (the one in ``zope.component.globalregistry.base``). + +To "fix" this and make the ZCA global APIs use the "current" :app:`Pyramid` +registry, you need to call :meth:`~pyramid.config.Configurator.hook_zca` within +your setup code. For example: .. code-block:: python :linenos: + :emphasize-lines: 5 from pyramid.config import Configurator @@ -194,9 +182,9 @@ For example: config.include('some.other.application') return config.make_wsgi_app() -We've added a line to our original startup code, line number 6, which -calls ``config.hook_zca()``. The effect of this line under the hood -is that an analogue of the following code is executed: +We've added a line to our original startup code, line number 5, which calls +``config.hook_zca()``. The effect of this line under the hood is that an +analogue of the following code is executed: .. code-block:: python :linenos: @@ -205,17 +193,15 @@ is that an analogue of the following code is executed: from pyramid.threadlocal import get_current_registry getSiteManager.sethook(get_current_registry) -This causes the ZCA global API to start using the :app:`Pyramid` -application registry in threads which are running a :app:`Pyramid` -request. +This causes the ZCA global API to start using the :app:`Pyramid` application +registry in threads which are running a :app:`Pyramid` request. -Calling ``hook_zca`` is usually sufficient to "fix" the problem of -being able to use the global ZCA API within a :app:`Pyramid` -application. However, it also means that a Zope application that is -running in the same process may start using the :app:`Pyramid` -global registry instead of the Zope global registry, effectively -inverting the original problem. In such a case, follow the steps in -the next section, :ref:`using_the_zca_global_registry`. +Calling ``hook_zca`` is usually sufficient to "fix" the problem of being able +to use the global ZCA API within a :app:`Pyramid` application. However, it +also means that a Zope application that is running in the same process may +start using the :app:`Pyramid` global registry instead of the Zope global +registry, effectively inverting the original problem. In such a case, follow +the steps in the next section, :ref:`using_the_zca_global_registry`. .. index:: single: get_current_registry @@ -224,14 +210,15 @@ the next section, :ref:`using_the_zca_global_registry`. .. _using_the_zca_global_registry: -Enabling the ZCA Global API by Using The ZCA Global Registry +Enabling the ZCA global API by using the ZCA global registry ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -You can tell your :app:`Pyramid` application to use the ZCA global -registry at startup time instead of constructing a new one: +You can tell your :app:`Pyramid` application to use the ZCA global registry at +startup time instead of constructing a new one: .. code-block:: python :linenos: + :emphasize-lines: 5-7 from zope.component import getGlobalSiteManager from pyramid.config import Configurator @@ -243,16 +230,14 @@ registry at startup time instead of constructing a new one: config.include('some.other.application') return config.make_wsgi_app() -Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves -the global ZCA component registry. Line 6 creates a -:term:`Configurator`, passing the global ZCA registry into its -constructor as the ``registry`` argument. Line 7 "sets up" the global -registry with Pyramid-specific registrations; this is code that is -normally executed when a registry is constructed rather than created, +Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves the global +ZCA component registry. Line 6 creates a :term:`Configurator`, passing the +global ZCA registry into its constructor as the ``registry`` argument. Line 7 +"sets up" the global registry with Pyramid-specific registrations; this is code +that is normally executed when a registry is constructed rather than created, but we must call it "by hand" when we pass an explicit registry. -At this point, :app:`Pyramid` will use the ZCA global registry -rather than creating a new application-specific registry; since by -default the ZCA global API will use this registry, things will work as -you might expect a Zope app to when you use the global ZCA API. - +At this point, :app:`Pyramid` will use the ZCA global registry rather than +creating a new application-specific registry. Since by default the ZCA global +API will use this registry, things will work as you might expect in a Zope app +when you use the global ZCA API. |
