diff options
Diffstat (limited to 'docs/narr')
30 files changed, 1361 insertions, 443 deletions
diff --git a/docs/narr/MyProject/README.txt b/docs/narr/MyProject/README.txt index 5e10949fc..c28d0d94a 100644 --- a/docs/narr/MyProject/README.txt +++ b/docs/narr/MyProject/README.txt @@ -1,4 +1 @@ MyProject README - - - diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini index 3a4758c44..d61da580f 100644 --- a/docs/narr/MyProject/development.ini +++ b/docs/narr/MyProject/development.ini @@ -41,6 +41,6 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s # End logging configuration diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py index 04e219e36..31b02cf02 100644 --- a/docs/narr/MyProject/myproject/__init__.py +++ b/docs/narr/MyProject/myproject/__init__.py @@ -1,12 +1,10 @@ from pyramid.config import Configurator -from myproject.resources import Root def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - config = Configurator(root_factory=Root, settings=settings) - config.add_view('myproject.views.my_view', - context='myproject.resources.Root', - renderer='myproject:templates/mytemplate.pt') - config.add_static_view('static', 'myproject:static') + config = Configurator(settings=settings) + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') + config.scan() return config.make_wsgi_app() diff --git a/docs/narr/MyProject/myproject/resources.py b/docs/narr/MyProject/myproject/resources.py deleted file mode 100644 index 3d811895c..000000000 --- a/docs/narr/MyProject/myproject/resources.py +++ /dev/null @@ -1,3 +0,0 @@ -class Root(object): - def __init__(self, request): - self.request = request diff --git a/docs/narr/MyProject/myproject/static/pylons.css b/docs/narr/MyProject/myproject/static/pylons.css index 33b21ac1a..c54499ddd 100644 --- a/docs/narr/MyProject/myproject/static/pylons.css +++ b/docs/narr/MyProject/myproject/static/pylons.css @@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;} h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;} h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;} html,body{width:100%;height:100%;} -body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;} +body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;} a{color:#1b61d6;text-decoration:none;} a:hover{color:#e88f00;text-decoration:underline;} body h1, @@ -31,19 +31,20 @@ body h2, body h3, body h4, body h5, -body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;} +body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;} #wrap{min-height:100%;} #header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;} #header{background:#000000;top:0;font-size:14px;} #footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;} .header,.footer{width:750px;margin-right:auto;margin-left:auto;} .wrapper{width:100%} -#top,#bottom{width:100%;} -#top{color:#000000;height:230px; -background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} +#top,#top-small,#bottom{width:100%;} +#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} +#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} #bottom{color:#222;background-color:#ffffff;} -.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;} +.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;} .top{padding-top:40px;} +.top-small{padding-top:10px;} #middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;} .app-welcome{margin-top:25px;} .app-name{color:#000000;font-weight:bold;} @@ -58,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;} form{border-style:none;} fieldset{border-style:none;} input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;} -input[type=text]{width:205px;} +input[type=text],input[type=password]{width:205px;} input[type=submit]{background-color:#ddd;font-weight:bold;} /*Opera Fix*/ body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;} diff --git a/docs/narr/MyProject/myproject/static/pyramid-small.png b/docs/narr/MyProject/myproject/static/pyramid-small.png Binary files differnew file mode 100644 index 000000000..a5bc0ade7 --- /dev/null +++ b/docs/narr/MyProject/myproject/static/pyramid-small.png diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt index 97f1e1aa3..0bfac946e 100644 --- a/docs/narr/MyProject/myproject/templates/mytemplate.pt +++ b/docs/narr/MyProject/myproject/templates/mytemplate.pt @@ -1,42 +1,29 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" - xml:lang="en" - xmlns:tal="http://xml.zope.org/namespaces/tal"> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>The Pyramid Web Application Development Framework</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> - <link rel="shortcut icon" - href="${request.static_url('myproject:static/favicon.ico')}" /> - <link rel="stylesheet" - href="${request.static_url('myproject:static/pylons.css')}" - type="text/css" media="screen" charset="utf-8" /> - <link rel="stylesheet" - href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&subset=latin" - type="text/css" media="screen" charset="utf-8" /> + <link rel="shortcut icon" href="/static/favicon.ico" /> + <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" /> + <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" /> <!--[if lte IE 6]> - <link rel="stylesheet" - href="${request.static_url('myproject:static/ie6.css')}" - type="text/css" media="screen" charset="utf-8" /> + <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top"> <div class="top align-center"> - <div> - <img src="${request.static_url('myproject:static/pyramid.png')}" - width="750" height="169" alt="pyramid"/> - </div> + <div><img src="/static/pyramid.png" width="750" height="169" alt="pyramid"/></div> </div> </div> <div id="middle"> <div class="middle align-center"> <p class="app-welcome"> - Welcome to <span class="app-name">${project}</span>, - an application generated by<br/> + Welcome to <span class="app-name">${project}</span>, an application generated by<br/> the Pyramid web application development framework. </p> </div> @@ -45,62 +32,45 @@ <div class="bottom"> <div id="left" class="align-right"> <h2>Search documentation</h2> - <form method="get" - action="http://docs.pylonsproject.org/pyramid/current/search.html"> - <input type="text" id="q" name="q" value="" /> - <input type="submit" id="x" value="Go" /> - </form> + <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html"> + <input type="text" id="q" name="q" value="" /> + <input type="submit" id="x" value="Go" /> + </form> </div> <div id="right" class="align-left"> <h2>Pyramid links</h2> <ul class="links"> <li> - <a href="http://pylonsproject.org"> - Pylons Website - </a> + <a href="http://pylonsproject.org">Pylons Website</a> </li> <li> - <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation"> - Narrative Documentation - </a> + <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#narrative-documentation">Narrative Documentation</a> </li> <li> - <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation"> - API Documentation - </a> + <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#reference-material">API Documentation</a> </li> <li> - <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials"> - Tutorials - </a> + <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#tutorials">Tutorials</a> </li> <li> - <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history"> - Change History - </a> + <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#detailed-change-history">Change History</a> </li> <li> - <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications"> - Sample Applications - </a> + <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#sample-applications">Sample Applications</a> </li> <li> - <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development"> - Support and Development - </a> + <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#support-and-development">Support and Development</a> </li> <li> - <a href="irc://irc.freenode.net#pyramid"> - IRC Channel - </a> + <a href="irc://irc.freenode.net#pyramid">IRC Channel</a> </li> - </ul> + </ul> </div> </div> </div> </div> <div id="footer"> - <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div> + <div class="footer">© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html> diff --git a/docs/narr/MyProject/myproject/tests.py b/docs/narr/MyProject/myproject/tests.py index 5fa710278..d8b764041 100644 --- a/docs/narr/MyProject/myproject/tests.py +++ b/docs/narr/MyProject/myproject/tests.py @@ -10,9 +10,7 @@ class ViewTests(unittest.TestCase): testing.tearDown() def test_my_view(self): - from myproject.views import my_view + from .views import my_view request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['project'], 'MyProject') - - diff --git a/docs/narr/MyProject/myproject/views.py b/docs/narr/MyProject/myproject/views.py index c43b34460..f571a5976 100644 --- a/docs/narr/MyProject/myproject/views.py +++ b/docs/narr/MyProject/myproject/views.py @@ -1,2 +1,5 @@ +from pyramid.view import view_config + +@view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): return {'project':'MyProject'} diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini index 9d025715d..97050e8fe 100644 --- a/docs/narr/MyProject/production.ini +++ b/docs/narr/MyProject/production.ini @@ -25,11 +25,11 @@ keys = console keys = generic [logger_root] -level = INFO +level = WARN handlers = console [logger_myproject] -level = INFO +level = WARN handlers = qualname = myproject diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst index 7b62b1a73..3a7bf2805 100644 --- a/docs/narr/advconfig.rst +++ b/docs/narr/advconfig.rst @@ -87,8 +87,8 @@ that ends something like this: Conflicting configuration actions For: ('view', None, '', None, <InterfaceClass pyramid.interfaces.IView>, None, None, None, None, None, False, None, None, None) - ('app.py', 14, '<module>', 'config.add_view(hello_world)') - ('app.py', 17, '<module>', 'config.add_view(hello_world)') + Line 14 of file app.py in <module>: 'config.add_view(hello_world)' + Line 17 of file app.py in <module>: 'config.add_view(goodbye_world)' This traceback is trying to tell us: @@ -115,6 +115,8 @@ Conflict detection happens for any kind of configuration: imperative configuration or configuration that results from the execution of a :term:`scan`. +.. _manually_resolving_conflicts: + Manually Resolving Conflicts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -397,76 +399,3 @@ constraints: the routes they imply require relative ordering. Such ordering constraints are not absolved by two-phase configuration. Routes are still added in configuration execution order. -.. index:: - single: add_directive - pair: configurator; adding directives - -.. _add_directive: - -Adding Methods to the Configurator via ``add_directive`` --------------------------------------------------------- - -Framework extension writers can add arbitrary methods to a -:term:`Configurator` by using the -:meth:`pyramid.config.Configurator.add_directive` method of the configurator. -This makes it possible to extend a Pyramid configurator in arbitrary ways, -and allows it to perform application-specific tasks more succinctly. - -The :meth:`~pyramid.config.Configurator.add_directive` method accepts two -positional arguments: a method name and a callable object. The callable -object is usually a function that takes the configurator instance as its -first argument and accepts other arbitrary positional and keyword arguments. -For example: - -.. code-block:: python - :linenos: - - from pyramid.events import NewRequest - from pyramid.config import Configurator - - def add_newrequest_subscriber(config, subscriber): - config.add_subscriber(subscriber, NewRequest). - - if __name__ == '__main__': - config = Configurator() - config.add_directive('add_newrequest_subscriber', - add_newrequest_subscriber) - -Once :meth:`~pyramid.config.Configurator.add_directive` is called, a user can -then call the method by its given name as if it were a built-in method of the -Configurator: - -.. code-block:: python - :linenos: - - def mysubscriber(event): - print event.request - - config.add_newrequest_subscriber(mysubscriber) - -A call to :meth:`~pyramid.config.Configurator.add_directive` is often -"hidden" within an ``includeme`` function within a "frameworky" package meant -to be included as per :ref:`including_configuration` via -:meth:`~pyramid.config.Configurator.include`. For example, if you put this -code in a package named ``pyramid_subscriberhelpers``: - -.. code-block:: python - :linenos: - - def includeme(config) - config.add_directive('add_newrequest_subscriber', - add_newrequest_subscriber) - -The user of the add-on package ``pyramid_subscriberhelpers`` would then be -able to install it and subsequently do: - -.. code-block:: python - :linenos: - - def mysubscriber(event): - print event.request - - from pyramid.config import Configurator - config = Configurator() - config.include('pyramid_subscriberhelpers') - config.add_newrequest_subscriber(mysubscriber) diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst index 0dc41e919..66ef46671 100644 --- a/docs/narr/commandline.rst +++ b/docs/narr/commandline.rst @@ -111,6 +111,7 @@ For a URL that doesn't match any views, ``pviews`` will simply print out a single: interactive shell single: IPython single: pshell + single: bpython .. _interactive_shell: @@ -267,23 +268,26 @@ exposed, and the request is configured to generate urls from the host .. index:: single: IPython + single: bpython -IPython -~~~~~~~ +.. _ipython_or_bpython: + +IPython or bpython +~~~~~~~~~~~~~~~~~~ -If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ installed in -the interpreter you use to invoke the ``pshell`` command, ``pshell`` will use -an IPython interactive shell instead of a standard Python interpreter shell. -If you don't want this to happen, even if you have IPython installed, you can -pass the ``--disable-ipython`` flag to the ``pshell`` command to use a -standard Python interpreter shell unconditionally. +If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ or +`bpython <http://bpython-interpreter.org/>`_ or both installed in +the interpreter you use to invoke the ``pshell`` command, ``pshell`` will +autodiscover them and use the first respectively found in this order : +IPython, bpython, standard Python interpreter. However you could +specifically invoke one of your choice with the ``-p choice`` or +``--python-shell choice`` option. .. code-block:: text - [chrism@vitaminf shellenv]$ ../bin/pshell --disable-ipython \ + [chrism@vitaminf shellenv]$ ../bin/pshell -p ipython | bpython | python \ development.ini#MyProject - .. index:: pair: routes; printing single: proutes diff --git a/docs/narr/extconfig.rst b/docs/narr/extconfig.rst new file mode 100644 index 000000000..5e7fe2753 --- /dev/null +++ b/docs/narr/extconfig.rst @@ -0,0 +1,364 @@ +.. index:: + single: extending configuration + +.. _extconfig_narr: + +Extending Pyramid Configuration +=============================== + +Pyramid allows you to extend its Configurator with custom directives. Custom +directives can use other directives, they can add a custom :term:`action`, +they can participate in :term:`conflict resolution`, and they can provide +some number of :term:`introspectable` objects. + +.. index:: + single: add_directive + pair: configurator; adding directives + +.. _add_directive: + +Adding Methods to the Configurator via ``add_directive`` +-------------------------------------------------------- + +Framework extension writers can add arbitrary methods to a +:term:`Configurator` by using the +:meth:`pyramid.config.Configurator.add_directive` method of the configurator. +Using :meth:`~pyramid.config.Configurator.add_directive` makes it possible to +extend a Pyramid configurator in arbitrary ways, and allows it to perform +application-specific tasks more succinctly. + +The :meth:`~pyramid.config.Configurator.add_directive` method accepts two +positional arguments: a method name and a callable object. The callable +object is usually a function that takes the configurator instance as its +first argument and accepts other arbitrary positional and keyword arguments. +For example: + +.. code-block:: python + :linenos: + + from pyramid.events import NewRequest + from pyramid.config import Configurator + + def add_newrequest_subscriber(config, subscriber): + config.add_subscriber(subscriber, NewRequest) + + if __name__ == '__main__': + config = Configurator() + config.add_directive('add_newrequest_subscriber', + add_newrequest_subscriber) + +Once :meth:`~pyramid.config.Configurator.add_directive` is called, a user can +then call the added directive by its given name as if it were a built-in +method of the Configurator: + +.. code-block:: python + :linenos: + + def mysubscriber(event): + print event.request + + config.add_newrequest_subscriber(mysubscriber) + +A call to :meth:`~pyramid.config.Configurator.add_directive` is often +"hidden" within an ``includeme`` function within a "frameworky" package meant +to be included as per :ref:`including_configuration` via +:meth:`~pyramid.config.Configurator.include`. For example, if you put this +code in a package named ``pyramid_subscriberhelpers``: + +.. code-block:: python + :linenos: + + def includeme(config): + config.add_directive('add_newrequest_subscriber', + add_newrequest_subscriber) + +The user of the add-on package ``pyramid_subscriberhelpers`` would then be +able to install it and subsequently do: + +.. code-block:: python + :linenos: + + def mysubscriber(event): + print event.request + + from pyramid.config import Configurator + config = Configurator() + config.include('pyramid_subscriberhelpers') + config.add_newrequest_subscriber(mysubscriber) + +Using ``config.action`` in a Directive +-------------------------------------- + +If a custom directive can't do its work exclusively in terms of existing +configurator methods (such as +:meth:`pyramid.config.Configurator.add_subscriber`, as above), the directive +may need to make use of the :meth:`pyramid.config.Configurator.action` +method. This method adds an entry to the list of "actions" that Pyramid will +attempt to process when :meth:`pyramid.config.Configurator.commit` is called. +An action is simply a dictionary that includes a :term:`discriminator`, +possibly a callback function, and possibly other metadata used by Pyramid's +action system. + +Here's an example directive which uses the "action" method: + +.. code-block:: python + :linenos: + + def add_jammyjam(config, jammyjam): + def register(): + config.registry.jammyjam = jammyjam + config.action('jammyjam', register) + + if __name__ == '__main__': + config = Configurator() + config.add_directive('add_jammyjam', add_jammyjam) + +Fancy, but what does it do? The action method accepts a number of arguments. +In the above directive named ``add_jammyjam``, we call +:meth:`~pyramid.config.Configurator.action` with two arguments: the string +``jammyjam`` is passed as the first argument named ``discriminator``, and the +closure function named ``register`` is passed as the second argument named +``callable``. + +When the :meth:`~pyramid.config.Configurator.action` method is called, it +appends an action to the list of pending configuration actions. All pending +actions with the same discriminator value are potentially in conflict with +one another (see :ref:`conflict_detection`). When the +:meth:`~pyramid.config.Configurator.commit` method of the Configurator is +called (either explicitly or as the result of calling +:meth:`~pyramid.config.Configurator.make_wsgi_app`), conflicting actions are +potentially automatically resolved as per +:ref:`automatic_conflict_resolution`. If a conflict cannot be automatically +resolved, a :exc:`ConfigurationConflictError` is raised and application +startup is prevented. + +In our above example, therefore, if a consumer of our ``add_jammyjam`` +directive did this: + +.. code-block:: python + + config.add_jammyjam('first') + config.add_jammyjam('second') + +When the action list was committed resulting from the set of calls above, our +user's application would not start, because the discriminators of the actions +generated by the two calls are in direct conflict. Automatic conflict +resolution cannot resolve the conflict (because no ``config.include`` is +involved), and the user provided no intermediate +:meth:`pyramid.config.Configurator.commit` call between the calls to +``add_jammyjam`` to ensure that the successive calls did not conflict with +each other. + +This demonstrates the purpose of the discriminator argument to the action +method: it's used to indicate a uniqueness constraint for an action. Two +actions with the same discriminator will conflict unless the conflict is +automatically or manually resolved. A discriminator can be any hashable +object, but it is generally a string or a tuple. *You use a discriminator to +declaratively ensure that the user doesn't provide ambiguous configuration +statements.* + +But let's imagine that a consumer of ``add_jammyjam`` used it in such a way +that no configuration conflicts are generated. + +.. code-block:: python + + config.add_jammyjam('first') + +What happens now? When the ``add_jammyjam`` method is called, an action is +appended to the pending actions list. When the pending configuration actions +are processed during :meth:`~pyramid.config.Configurator.commit`, and no +conflicts occur, the *callable* provided as the second argument to the +:meth:`~pyramid.config.Configurator.action` method within ``add_jammyjam`` is +called with no arguments. The callable in ``add_jammyjam`` is the +``register`` closure function. It simply sets the value +``config.registry.jammyjam`` to whatever the user passed in as the +``jammyjam`` argument to the ``add_jammyjam`` function. Therefore, the +result of the user's call to our directive will set the ``jammyjam`` +attribute of the registry to the string ``first``. *A callable is used by a +directive to defer the result of a user's call to the directive until +conflict detection has had a chance to do its job*. + +Other arguments exist to the :meth:`~pyramid.config.Configurator.action` +method, including ``args``, ``kw``, ``order``, and ``introspectables``. + +``args`` and ``kw`` exist as values, which, if passed, will be used as +arguments to the ``callable`` function when it is called back. For example +our directive might use them like so: + +.. code-block:: python + :linenos: + + def add_jammyjam(config, jammyjam): + def register(*arg, **kw): + config.registry.jammyjam_args = arg + config.registry.jammyjam_kw = kw + config.registry.jammyjam = jammyjam + config.action('jammyjam', register, args=('one',), kw={'two':'two'}) + +In the above example, when this directive is used to generate an action, and +that action is committed, ``config.registry.jammyjam_args`` will be set to +``('one',)`` and ``config.registry.jammyjam_kw`` will be set to +``{'two':'two'}``. ``args`` and ``kw`` are honestly not very useful when +your ``callable`` is a closure function, because you already usually have +access to every local in the directive without needing them to be passed +back. They can be useful, however, if you don't use a closure as a callable. + +``order`` is a crude order control mechanism. ``order`` defaults to the +integer ``0``; it can be set to any other integer. All actions that share an +order will be called before other actions that share a higher order. This +makes it possible to write a directive with callable logic that relies on the +execution of the callable of another directive being done first. For +example, Pyramid's :meth:`pyramid.config.Configurator.add_view` directive +registers an action with a higher order than the +:meth:`pyramid.config.Configurator.add_route` method. Due to this, the +``add_view`` method's callable can assume that, if a ``route_name`` was +passed to it, that a route by this name was already registered by +``add_route``, and if such a route has not already been registered, it's a +configuration error (a view that names a nonexistent route via its +``route_name`` parameter will never be called). + +``introspectables`` is a sequence of :term:`introspectable` objects. You can +pass a sequence of introspectables to the +:meth:`~pyramid.config.Configurator.action` method, which allows you to +augment Pyramid's configuration introspection system. + +.. _introspection: + +Adding Configuration Introspection +---------------------------------- + +.. note:: + + The introspection subsystem is new in Pyramid 1.3. + +Pyramid provides a configuration introspection system that can be used by +debugging tools to provide visibility into the configuration of a running +application. + +All built-in Pyramid directives (such as +:meth:`pyramid.config.Configurator.add_view` and +:meth:`pyramid.config.Configurator.add_route`) register a set of +introspectables when called. For example, when you register a view via +``add_view``, the directive registers at least one introspectable: an +introspectable about the view registration itself, providing human-consumable +values for the arguments it was passed. You can later use the introspection +query system to determine whether a particular view uses a renderer, or +whether a particular view is limited to a particular request method, or which +routes a particular view is registered against. The Pyramid "debug toolbar" +makes use of the introspection system in various ways to display information +to Pyramid developers. + +Introspection values are set when a sequence of :term:`introspectable` +objects is passed to the :meth:`~pyramid.config.Configurator.action` method. +Here's an example of a directive which uses introspectables: + +.. code-block:: python + :linenos: + + def add_jammyjam(config, value): + def register(): + config.registry.jammyjam = value + intr = config.introspectable(category_name='jammyjams', + discriminator='jammyjam', + title='a jammyjam', + type_name=None) + intr['value'] = value + config.action('jammyjam', register, introspectables=(intr,)) + + if __name__ == '__main__': + config = Configurator() + config.add_directive('add_jammyjam', add_jammyjam) + +If you notice, the above directive uses the ``introspectable`` attribute of a +Configurator (:attr:`pyramid.config.Configurator.introspectable`) to create +an introspectable object. The introspectable object's constructor requires +at least four arguments: the ``category_name``, the ``discriminator``, the +``title``, and the ``type_name``. + +The ``category_name`` is a string representing the logical category for this +introspectable. Usually the category_name is a pluralization of the type of +object being added via the action. + +The ``discriminator`` is a value unique **within the category** (unlike the +action discriminator, which must be unique within the entire set of actions). +It is typically a string or tuple representing the values unique to this +introspectable within the category. It is used to generate links and as part +of a relationship-forming target for other introspectables. + +The ``title`` is a human-consumable string that can be used by introspection +system frontends to show a friendly summary of this introspectable. + +The ``type_name`` is a value that can be used to subtype this introspectable +within its category for for sorting and presentation purposes. It can be any +value. + +An introspectable is also dictionary-like. It can contain any set of +key/value pairs, typically related to the arguments passed to its related +directive. While the category_name, discriminator, title and type_name are +*metadata* about the introspectable, the values provided as key/value pairs +are the actual data provided by the introspectable. In the above example, we +set the ``value`` key to the value of the ``value`` argument passed to the +directive. + +Our directive above mutates the introspectable, and passes it in to the +``action`` method as the first element of a tuple as the value of the +``introspectable`` keyword argument. This associates this introspectable +with the action. Introspection tools will then display this introspectable +in their index. + +Introspectable Relationships +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Two introspectables may have relationships between each other. + +.. code-block:: python + :linenos: + + def add_jammyjam(config, value, template): + def register(): + config.registry.jammyjam = (value, template) + intr = config.introspectable(category_name='jammyjams', + discriminator='jammyjam', + title='a jammyjam', + type_name=None) + intr['value'] = value + tmpl_intr = config.introspectable(category_name='jammyjam templates', + discriminator=template, + title=template, + type_name=None) + tmpl_intr['value'] = template + intr.relate('jammyjam templates', template) + config.action('jammyjam', register, introspectables=(intr, tmpl_intr)) + + if __name__ == '__main__': + config = Configurator() + config.add_directive('add_jammyjam', add_jammyjam) + +In the above example, the ``add_jammyjam`` directive registers two +introspectables. The first is related to the ``value`` passed to the +directive; the second is related to the ``template`` passed to the directive. +If you believe a concept within a directive is important enough to have its +own introspectable, you can cause the same directive to register more than +one introspectable, registering one introspectable for the "main idea" and +another for a related concept. + +The call to ``intr.relate`` above +(:meth:`pyramid.interfaces.IIntrospectable.relate`) is passed two arguments: +a category name and a directive. The example above effectively indicates +that the directive wishes to form a relationship between the ``intr`` +introspectable and the ``tmpl_intr`` introspectable; the arguments passed to +``relate`` are the category name and discriminator of the ``tmpl_intr`` +introspectable. + +Relationships need not be made between two introspectables created by the +same directive. Instead, a relationship can be formed between an +introspectable created in one directive and another introspectable created in +another by calling ``relate`` on either side with the other directive's +category name and discriminator. An error will be raised at configuration +commit time if you attempt to relate an introspectable with another +nonexistent introspectable, however. + +Introspectable relationships will show up in frontend system renderings of +introspection values. For example, if a view registration names a route +name, the introspectable related to the view callable will show a reference +to the route to which it relates to and vice versa. diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 9c96248f2..c464203f0 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -200,8 +200,8 @@ like this: overridden elements, such as templates and static assets as necessary. - Install the new package into the same Python environment as the original - application (e.g. ``python setup.py develop`` or ``python setup.py - install``). + application (e.g. ``$myvenv/bin/python setup.py develop`` or + ``$myvenv/bin/python setup.py install``). - Change the ``main`` function in the new package's ``__init__.py`` to include the original :app:`Pyramid` application's configuration functions via diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index 45d65402c..c082f616b 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -71,11 +71,10 @@ named ``hello_world``. :linenos: :pyobject: hello_world -This function doesn't do anything very difficult. The functions accepts a -single argument (``request``). The ``hello_world`` function returns an -instance of the :class:`pyramid.response.Response`. The single argument to -the class' constructor is value computed from arguments matched from the url -route. This value becomes the body of the response. +The function accepts a single argument (``request``) and it returns an +instance of the :class:`pyramid.response.Response` class. The single +argument to the class' constructor is a string computed from parameters +matched from the URL. This value becomes the body of the response. This function is known as a :term:`view callable`. A view callable accepts a single argument, ``request``. It is expected to return a @@ -134,7 +133,7 @@ Using the ``if`` clause is necessary -- or at least best practice -- because code in a Python ``.py`` file may be eventually imported via the Python ``import`` statement by another ``.py`` file. ``.py`` files that are imported by other ``.py`` files are referred to as *modules*. By using the -``if __name__ == 'main':`` idiom, the script above is indicating that it does +``if __name__ == '__main__':`` idiom, the script above is indicating that it does not want the code within the ``if`` statement to execute if this module is imported from another; the code within the ``if`` block should only be run during a direct script execution. @@ -157,7 +156,7 @@ Adding Configuration :lines: 10-11 First line above calls the :meth:`pyramid.config.Configurator.add_route` -method, which registers a :term:`route` to match any url path that begins +method, which registers a :term:`route` to match any URL path that begins with ``/hello/`` followed by a string. The second line, ``config.add_view(hello_world, route_name='hello')``, @@ -208,7 +207,7 @@ WSGI Application Serving Finally, we actually serve the application to requestors by starting up a WSGI server. We happen to use the :func:`paste.httpserver.serve` WSGI server runner, passing it the ``app`` object (a :term:`router`) as the application -we wish to serve. We also pass in an argument ``host=='0.0.0.0'``, meaning +we wish to serve. We also pass in an argument ``host='0.0.0.0'``, meaning "listen on all TCP interfaces." By default, the HTTP server listens only on the ``127.0.0.1`` interface, which is problematic if you're running the server on a remote system and you wish to access it with a web browser diff --git a/docs/narr/helloworld.py b/docs/narr/helloworld.py index 93a403a13..7c26c8cdc 100644 --- a/docs/narr/helloworld.py +++ b/docs/narr/helloworld.py @@ -10,6 +10,6 @@ if __name__ == '__main__': config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') app = config.make_wsgi_app() - server = make_server('0.0.0.0', 8080) + server = make_server('0.0.0.0', 8080, app) server.serve_forever() diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst index c2ecba9bb..e261f9a11 100644 --- a/docs/narr/i18n.rst +++ b/docs/narr/i18n.rst @@ -149,7 +149,7 @@ generated by using it. For example: from pyramid.i18n import TranslationStringFactory _ = TranslationStringFactory('pyramid') - ts = _('Add ${number}', msgid='add-number', mapping={'number':1}) + ts = _('add-number', default='Add ${number}', mapping={'number':1}) .. note:: We assigned the translation string factory to the name ``_``. This is a convention which will be supported by translation @@ -170,7 +170,7 @@ to: :linenos: from pyramid.i18n import TranslationString as _ - ts = _('Add ${number}', msgid='add-number', mapping={'number':1}, + ts = _('add-number', default='Add ${number}', mapping={'number':1}, domain='pyramid') You can set up your own translation string factory much like the one @@ -185,7 +185,7 @@ do something like this: from pyramid.i18n import TranslationStringFactory _ = TranslationStringFactory('form') - ts = _('Add ${number}', msgid='add-number', mapping={'number':1}) + ts = _('add-number', default='Add ${number}', mapping={'number':1}) Creating a unique domain for your application via a translation string factory is best practice. Using your own unique translation domain @@ -249,7 +249,7 @@ GNU gettext uses three types of files in the translation framework, The tools for working with :term:`gettext` translation files related to a :app:`Pyramid` application is :term:`Babel` and :term:`Lingua`. Lingua is a -Balel extension that provides support for scraping i18n references out of +Babel extension that provides support for scraping i18n references out of Python and Chameleon files. .. index:: @@ -347,7 +347,7 @@ extract the messages: $ cd /place/where/myapplication/setup.py/lives $ mkdir -p myapplication/locale - $ python setup.py extract_messages + $ $myvenv/bin/python setup.py extract_messages The message catalog ``.pot`` template will end up in: @@ -439,7 +439,7 @@ init_catalog`` command: .. code-block:: text $ cd /place/where/myapplication/setup.py/lives - $ python setup.py init_catalog -l es + $ $myvenv/bin/python setup.py init_catalog -l es By default, the message catalog ``.po`` file will end up in: @@ -471,7 +471,7 @@ Then use the ``setup.py update_catalog`` command. .. code-block:: text $ cd /place/where/myapplication/setup.py/lives - $ python setup.py update_catalog + $ $myvenv/bin/python setup.py update_catalog .. index:: pair: compiling; message catalog @@ -487,7 +487,7 @@ translations, compile ``.po`` files to ``.mo`` files: .. code-block:: text $ cd /place/where/myapplication/setup.py/lives - $ python setup.py compile_catalog + $ $myvenv/bin/python setup.py compile_catalog This will create a ``.mo`` file for each ``.po`` file in your application. As long as the :term:`translation directory` in which diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 66bcea706..3de4d6e27 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -9,19 +9,18 @@ Installing :app:`Pyramid` Before You Install ------------------ -You will need `Python <http://python.org>`_ version 2.5 or better to +You will need `Python <http://python.org>`_ version 2.6 or better to run :app:`Pyramid`. .. sidebar:: Python Versions - As of this writing, :app:`Pyramid` has been tested under Python 2.5.5, - Python 2.6.6, and Python 2.7.2. :app:`Pyramid` does not run under any - version of Python before 2.5, and does not yet run under Python 3.X. + As of this writing, :app:`Pyramid` has been tested under Python 2.6.6, + Python 2.7.2, and Python 3.2. :app:`Pyramid` does not run under any + version of Python before 2.6. :app:`Pyramid` is known to run on all popular UNIX-like systems such as Linux, MacOS X, and FreeBSD as well as on Windows platforms. It is also -known to run on Google's App Engine, :term:`PyPy` (1.5 and 1.6), and -:term:`Jython` (2.5.2). +known to run on Google's App Engine, and :term:`PyPy` (1.6+). :app:`Pyramid` installation does not require the compilation of any C code, so you need only a Python interpreter that meets the @@ -325,25 +324,6 @@ Installing :app:`Pyramid` on Google App Engine :ref:`appengine_tutorial` documents the steps required to install a :app:`Pyramid` application on Google App Engine. -.. index:: - single: installing on Jython - -Installing :app:`Pyramid` on Jython --------------------------------------- - -:app:`Pyramid` is known to work under :term:`Jython` version 2.5.1. -Install :term:`Jython`, and then follow the installation steps for -:app:`Pyramid` on your platform described in one of the sections -entitled :ref:`installing_unix` or :ref:`installing_windows` above, -replacing the ``python`` command with ``jython`` as necessary. The -steps are exactly the same except you should use the ``jython`` -command name instead of the ``python`` command name. - -One caveat exists to using :app:`Pyramid` under Jython: the :term:`Chameleon` -templating engine does not work on Jython. However, the :term:`Mako` -templating system, which is also included with Pyramid, does work under -Jython; use it instead. - What Gets Installed ------------------- diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 547f88ef3..7c6ad00f3 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -219,7 +219,7 @@ that the Pyramid core doesn't. Add-on packages already exist which let you easily send email, let you use the Jinja2 templating system, let you use XML-RPC or JSON-RPC, let you integrate with jQuery Mobile, etc. -Examples: https://docs.pylonsproject.org/docs/pyramid.html#pyramid-add-on-documentation +Examples: http://docs.pylonsproject.org/docs/pyramid.html#pyramid-add-on-documentation Class-based and function-based views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -808,11 +808,11 @@ Every release of Pyramid has 100% statement coverage via unit and integration tests, as measured by the ``coverage`` tool available on PyPI. It also has greater than 95% decision/condition coverage as measured by the ``instrumental`` tool available on PyPI. It is automatically tested by the -Jenkins tool on Python 2.5, Python 2.6, Python 2.7, Jython and PyPy after -each commit to its GitHub repository. Official Pyramid add-ons are held to a -similar testing standard. We still find bugs in Pyramid and its official -add-ons, but we've noticed we find a lot more of them while working on other -projects that don't have a good testing regime. +Jenkins tool on Python 2.6, Python 2.7, Python 3.2 and PyPy after each commit +to its GitHub repository. Official Pyramid add-ons are held to a similar +testing standard. We still find bugs in Pyramid and its official add-ons, +but we've noticed we find a lot more of them while working on other projects +that don't have a good testing regime. Example: http://jenkins.pylonsproject.org/ @@ -842,7 +842,7 @@ official narrative docs. In any case, the Pyramid documentation is comprehensive. Example: The rest of this documentation and the cookbook at -https://docs.pylonsproject.org/projects/pyramid_cookbook/dev/ . +http://docs.pylonsproject.org/projects/pyramid_cookbook/dev/ . .. index:: single: Pylons Project diff --git a/docs/narr/introspector.rst b/docs/narr/introspector.rst new file mode 100644 index 000000000..11d779854 --- /dev/null +++ b/docs/narr/introspector.rst @@ -0,0 +1,557 @@ +.. index:: + single: introspection + single: introspector + +.. _using_introspection: + +Pyramid Configuration Introspection +=================================== + +When Pyramid starts up, each call to a :term:`configuration directive` causes +one or more :term:`introspectable` objects to be registered with an +:term:`introspector`. The introspector can be queried by application code to +obtain information about the configuration of the running application. This +feature is useful for debug toolbars, command-line scripts which show some +aspect of configuration, and for runtime reporting of startup-time +configuration settings. + +.. warning:: + + Introspection is new in Pyramid 1.3. + +Using the Introspector +---------------------- + +Here's an example of using Pyramid's introspector from within a view +callable: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + from pyramid.response import Response + + @view_config(route_name='bar') + def route_accepts(request): + introspector = request.registry.introspector + route_name = request.matched_route.name + route_intr = introspector.get('routes', route_name) + return Response(str(route_intr['pattern'])) + +This view will return a response that contains the "pattern" argument +provided to the ``add_route`` method of the route which matched when the view +was called. It uses the :meth:`pyramid.interfaces.IIntrospector.get` method +to return an introspectable in the category ``routes`` with a +:term:`discriminator` equal to the matched route name. It then uses the +returned introspectable to obtain a "pattern" value. + +The introspectable returned by the query methods of the introspector has +methods and attributes described by +:class:`pyramid.interfaces.IIntrospectable`. In particular, the +:meth:`~pyramid.interfaces.IIntrospector.get`, +:meth:`~pyramid.interfaces.IIntrospector.get_category`, +:meth:`~pyramid.interfaces.IIntrospector.categories`, +:meth:`~pyramid.interfaces.IIntrospector.categorized`, and +:meth:`~pyramid.interfaces.IIntrospector.related` methods of an introspector +can be used to query for introspectables. + +Introspectable Objects +---------------------- + +Introspectable objects are returned from query methods of an introspector. +Each introspectable object implements the attributes and methods +documented at :class:`pyramid.interfaces.IIntrospectable`. + +The important attributes shared by all introspectables are the following: + +``title`` + + A human-readable text title describing the introspectable + +``category_name`` + + A text category name describing the introspection category to which this + introspectable belongs. It is often a plural if there are expected to be + more than one introspectable registered within the category. + +``discriminator`` + + A hashable object representing the unique value of this introspectable + within its category. + +``discriminator_hash`` + + The integer hash of the discriminator (useful for using in HTML links). + +``type_name`` + + The text name of a subtype within this introspectable's category. If there + is only one type name in this introspectable's category, this value will + often be a singular version of the category name but it can be an arbitrary + value. + +``action_info`` + + An object describing the directive call site which caused this + introspectable to be registered; contains attributes described in + :class:`pyramid.interfaces.IActionInfo`. + +Besides having the attributes described above, an introspectable is a +dictionary-like object. An introspectable can be queried for data values via +its ``__getitem__``, ``get``, ``keys``, ``values``, or ``items`` methods. +For example: + +.. code-block:: python + :linenos: + + route_intr = introspector.get('routes', 'edit_user') + pattern = route_intr['pattern'] + +Pyramid Introspection Categories +-------------------------------- + +The list of concrete introspection categories provided by built-in Pyramid +configuration directives follows. Add-on packages may supply other +introspectables in categories not described here. + +``subscribers`` + + Each introspectable in the ``subscribers`` category represents a call to + :meth:`pyramid.config.Configurator.add_subscriber` (or the decorator + equivalent); each will have the following data. + + ``subscriber`` + + The subscriber callable object (the resolution of the ``subscriber`` + argument passed to ``add_susbcriber``). + + ``interfaces`` + + A sequence of interfaces (or classes) that are subscribed to (the + resolution of the ``ifaces`` argument passed to ``add_subscriber``). + +``response adapters`` + + Each introspectable in the ``response adapters`` category represents a call + to :meth:`pyramid.config.Configurator.add_response_adapter` (or a decorator + equivalent); each will have the following data. + + ``adapter`` + + The adapter object (the resolved ``adapter`` argument to + ``add_response_adapter``). + + ``type`` + + The resolved ``type_or_iface`` argument passed to + ``add_response_adapter``. + +``root factories`` + + Each introspectable in the ``root factories`` category represents a call to + :meth:`pyramid.config.Configurator.set_root_factory` (or the Configurator + constructor equivalent) *or* a ``factory`` argument passed to + :meth:`pyramid.config.Configurator.add_route`; each will have the following + data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_root_factory``). + + ``route_name`` + + The name of the route which will use this factory. If this is the + *default* root factory (if it's registered during a call to + ``set_root_factory``), this value will be ``None``. + +``session factory`` + + Only one introspectable will exist in the ``session factory`` category. It + represents a call to :meth:`pyramid.config.Configurator.set_session_factory` + (or the Configurator constructor equivalent); it will have the following + data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_session_factory``). + +``request factory`` + + Only one introspectable will exist in the ``request factory`` category. It + represents a call to :meth:`pyramid.config.Configurator.set_request_factory` + (or the Configurator constructor equivalent); it will have the following + data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_request_factory``). + +``locale negotiator`` + + Only one introspectable will exist in the ``locale negotiator`` category. + It represents a call to + :meth:`pyramid.config.Configurator.set_locale_negotiator` (or the + Configurator constructor equivalent); it will have the following data. + + ``negotiator`` + + The factory object (the resolved ``negotiator`` argument to + ``set_locale_negotiator``). + +``renderer factories`` + + Each introspectable in the ``renderer factories`` category represents a + call to :meth:`pyramid.config.Configurator.add_renderer` (or the + Configurator constructor equivalent); each will have the following data. + + ``name`` + + The name of the renderer (the value of the ``name`` argument to + ``add_renderer``). + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``add_renderer``). + +``renderer globals factory`` + + There will be one and only one introspectable in the ``renderer globals + factory`` category. It represents a call to + :meth:`pyramid.config.Configurator.set_renderer_globals_factory`; it will + have the following data. + + ``factory`` + + The factory object (the resolved ``factory`` argument to + ``set_renderer_globals_factory``). + +``routes`` + + Each introspectable in the ``routes`` category represents a call to + :meth:`pyramid.config.Configurator.add_route`; each will have the following + data. + + ``name`` + + The ``name`` argument passed to ``add_route``. + + ``pattern`` + + The ``pattern`` argument passed to ``add_route``. + + ``factory`` + + The (resolved) ``factory`` argument passed to ``add_route``. + + ``xhr`` + + The ``xhr`` argument passed to ``add_route``. + + ``request_method`` + + The ``request_method`` argument passed to ``add_route``. + + ``request_methods`` + + A sequence of request method names implied by the ``request_method`` + argument passed to ``add_route`` or the value ``None`` if a + ``request_method`` argument was not supplied. + + ``path_info`` + + The ``path_info`` argument passed to ``add_route``. + + ``request_param`` + + The ``request_param`` argument passed to ``add_route``. + + ``header`` + + The ``header`` argument passed to ``add_route``. + + ``accept`` + + The ``accept`` argument passed to ``add_route``. + + ``traverse`` + + The ``traverse`` argument passed to ``add_route``. + + ``custom_predicates`` + + The ``custom_predicates`` argument passed to ``add_route``. + + ``pregenerator`` + + The ``pregenerator`` argument passed to ``add_route``. + + ``static`` + + The ``static`` argument passed to ``add_route``. + + ``use_global_views`` + + The ``use_global_views`` argument passed to ``add_route``. + + ``object`` + + The :class:`pyramid.interfaces.IRoute` object that is used to perform + matching and generation for this route. + +``authentication policy`` + + There will be one and only one introspectable in the ``authentication + policy`` category. It represents a call to the + :meth:`pyramid.config.Configurator.set_authentication_policy` method (or + its Configurator constructor equivalent); it will have the following data. + + ``policy`` + + The policy object (the resolved ``policy`` argument to + ``set_authentication_policy``). + +``authorization policy`` + + There will be one and only one introspectable in the ``authorization + policy`` category. It represents a call to the + :meth:`pyramid.config.Configurator.set_authorization_policy` method (or its + Configurator constructor equivalent); it will have the following data. + + ``policy`` + + The policy object (the resolved ``policy`` argument to + ``set_authorization_policy``). + +``default permission`` + + There will be one and only one introspectable in the ``default permission`` + category. It represents a call to the + :meth:`pyramid.config.Configurator.set_default_permission` method (or its + Configurator constructor equivalent); it will have the following data. + + ``value`` + + The permission name passed to ``set_default_permission``. + +``views`` + + Each introspectable in the ``views`` category represents a call to + :meth:`pyramid.config.Configurator.add_view`; each will have the following + data. + + ``name`` + + The ``name`` argument passed to ``add_view``. + + ``context`` + + The (resolved) ``context`` argument passed to ``add_view``. + + ``containment`` + + The (resolved) ``containment`` argument passed to ``add_view``. + + ``request_param`` + + The ``request_param`` argument passed to ``add_view``. + + ``request_methods`` + + A sequence of request method names implied by the ``request_method`` + argument passed to ``add_view`` or the value ``None`` if a + ``request_method`` argument was not supplied. + + ``route_name`` + + The ``route_name`` argument passed to ``add_view``. + + ``attr`` + + The ``attr`` argument passed to ``add_view``. + + ``xhr`` + + The ``xhr`` argument passed to ``add_view``. + + ``accept`` + + The ``accept`` argument passed to ``add_view``. + + ``header`` + + The ``header`` argument passed to ``add_view``. + + ``path_info`` + + The ``path_info`` argument passed to ``add_view``. + + ``match_param`` + + The ``match_param`` argument passed to ``add_view``. + + ``callable`` + + The (resolved) ``view`` argument passed to ``add_view``. Represents the + "raw" view callable. + + ``derived_callable`` + + The view callable derived from the ``view`` argument passed to + ``add_view``. Represents the view callable which Pyramid itself calls + (wrapped in security and other wrappers). + + ``mapper`` + + The (resolved) ``mapper`` argument passed to ``add_view``. + + ``decorator`` + + The (resolved) ``decorator`` argument passed to ``add_view``. + +``permissions`` + + Each introspectable in the ``permissions`` category represents a call to + :meth:`pyramid.config.Configurator.add_view` that has an explicit + ``permission`` argument to *or* a call to + :meth:`pyramid.config.Configurator.set_default_permission`; each will have + the following data. + + ``value`` + + The permission name passed to ``add_view`` or ``set_default_permission``. + +``templates`` + + Each introspectable in the ``templates`` category represents a call to + :meth:`pyramid.config.Configurator.add_view` that has a ``renderer`` + argument which points to a template; each will have the following data. + + ``name`` + + The renderer's name (a string). + + ``type`` + + The renderer's type (a string). + + ``renderer`` + + The :class:`pyramid.interfaces.IRendererInfo` object which represents + this template's renderer. + +``view mapper`` + + Each introspectable in the ``permissions`` category represents a call to + :meth:`pyramid.config.Configurator.add_view` that has an explicit + ``mapper`` argument to *or* a call to + :meth:`pyramid.config.Configurator.set_view_mapper`; each will have + the following data. + + ``mapper`` + + The (resolved) ``mapper`` argument passed to ``add_view`` or + ``set_view_mapper``. + +``asset overrides`` + + Each introspectable in the ``asset overrides`` category represents a call + to :meth:`pyramid.config.Configurator.override_asset`; each will have the + following data. + + ``to_override`` + + The ``to_override`` argument (an asset spec) passed to + ``override_asset``. + + ``override_with`` + + The ``override_with`` argument (an asset spec) passed to + ``override_asset``. + +``translation directories`` + + Each introspectable in the ``asset overrides`` category represents an + individual element in a ``specs`` argument passed to to + :meth:`pyramid.config.Configurator.add_translation_dirs`; each will have + the following data. + + ``directory`` + + The absolute path of the translation directory. + + ``spec`` + + The asset specification passed to ``add_translation_dirs``. + +``tweens`` + + Each introspectable in the ``tweens`` category represents a call to + :meth:`pyramid.config.Configurator.add_tween`; each will have the following + data. + + ``name`` + + The dotted name to the tween factory as a string (passed as + the ``tween_factory`` argument to ``add_tween``). + + ``factory`` + + The (resolved) tween factory object. + + ``type`` + + ``implict`` or ``explicit`` as a string. + + ``under`` + + The ``under`` argument passed to ``add_tween`` (a string). + + ``over`` + + The ``over`` argument passed to ``add_tween`` (a string). + +``static views`` + + Each introspectable in the ``static views`` category represents a call to + :meth:`pyramid.config.Configurator.add_static_view`; each will have the + following data. + + ``name`` + + The ``name`` argument provided to ``add_static_view``. + + ``spec`` + + A normalized version of the ``spec`` argument provided to + ``add_static_view``. + +Introspection in the Toolbar +---------------------------- + +The Pyramid debug toolbar (part of the ``pyramid_debugtoolbar`` package) +provides a canned view of all registered introspectables and their +relationships. It looks something like this: + +.. image:: tb_introspector.png + +Disabling Introspection +----------------------- + +You can disable Pyramid introspection by passing the object +:attr:`pyramid.registry.noop_introspector` to the :term:`Configurator` +constructor in your application setup: + +.. code-block:: python + + from pyramid.config import Configurator + from pyramid.registry import noop_introspector + config = Configurator(..., introspector=noop_introspector) + +When the noop introspector is active, all introspectables generated by +configuration directives are thrown away. A noop introspector behaves just +like a "real" introspector, but the methods of a noop introspector do nothing +and return null values. diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst index 5377d0c66..044655c1f 100644 --- a/docs/narr/logging.rst +++ b/docs/narr/logging.rst @@ -16,6 +16,8 @@ how to send log messages to loggers that you've configured. a third-party scaffold which does not create these files, the configuration information in this chapter will not be applicable. +.. _logging_config: + Logging Configuration --------------------- @@ -291,7 +293,7 @@ Logging Exceptions To log (or email) exceptions generated by your :app:`Pyramid` application, use the :term:`pyramid_exclog` package. Details about its configuration are in its `documentation -<https://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_. +<http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_. Request Logging with Paste's TransLogger ---------------------------------------- diff --git a/docs/narr/muchadoabouttraversal.rst b/docs/narr/muchadoabouttraversal.rst index a948e57cc..4a249ed0d 100644 --- a/docs/narr/muchadoabouttraversal.rst +++ b/docs/narr/muchadoabouttraversal.rst @@ -4,7 +4,9 @@ Much Ado About Traversal ======================== -.. note:: This chapter was adapted, with permission, from a blog post by `Rob +.. note:: + + This chapter was adapted, with permission, from a blog post by `Rob Miller <http://blog.nonsequitarian.org/>`_, originally published at http://blog.nonsequitarian.org/2010/much-ado-about-traversal/ . diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 4f96448af..af8714573 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -28,7 +28,6 @@ as part of Pyramid. single: starter scaffold single: zodb scaffold single: alchemy scaffold - single: routesalchemy scaffold .. _additional_paster_scaffolds: @@ -47,27 +46,17 @@ each other on a number of axes: The included scaffolds are these: ``starter`` - URL mapping via :term:`traversal` and no persistence mechanism. + URL mapping via :term:`URL dispatch` and no persistence mechanism. ``zodb`` - URL mapping via :term:`traversal` and persistence via :term:`ZODB`. - -``routesalchemy`` - URL mapping via :term:`URL dispatch` and persistence via - :term:`SQLAlchemy` + URL mapping via :term:`traversal` and persistence via :term:`ZODB`. *Note + that, as of this writing, this scaffold will not run under Python 3, only + under Python 2.* ``alchemy`` - URL mapping via :term:`traversal` and persistence via + URL mapping via :term:`URL dispatch` and persistence via :term:`SQLAlchemy` -.. note:: - -Rather than use any of the above scaffolds, Pylons 1 users may feel more -comfortable installing the :term:`Akhet` development environment, which -provides a scaffold named ``akhet``. This scaffold configures a Pyramid -application in a "Pylons-esque" way, including the use of a :term:`view -handler` to map URLs to code (a handler is much like a Pylons "controller"). - .. index:: single: creating a project single: project @@ -98,18 +87,18 @@ Or on Windows: The above command uses the ``pcreate`` command to create a project with the ``starter`` scaffold. To use a different scaffold, such as -``routesalchemy``, you'd just change the ``-s`` argument value. For example, +``alchemy``, you'd just change the ``-s`` argument value. For example, on UNIX: .. code-block:: text - $ bin/pcreate -s routesalchemy MyProject + $ bin/pcreate -s alchemy MyProject Or on Windows: .. code-block:: text - $ Scripts\pcreate routesalchemy MyProject + $ Scripts\pcreate alchemy MyProject Here's sample output from a run of ``pcreate`` on UNIX for a project we name ``MyProject``: @@ -386,7 +375,6 @@ structure: |-- MANIFEST.in |-- myproject | |-- __init__.py - | |-- resources.py | |-- static | | |-- favicon.ico | | |-- logo.png @@ -686,8 +674,6 @@ The ``myproject`` :term:`package` lives inside the ``MyProject`` ``main`` function which is used as a entry point for commands such as ``pserve``, ``pshell``, ``pviews``, and others. -#. A ``resources.py`` module, which contains :term:`resource` code. - #. A ``templates`` directory, which contains :term:`Chameleon` (or other types of) templates. @@ -723,34 +709,25 @@ also informs Python that the directory which contains it is a *package*. #. Line 1 imports the :term:`Configurator` class from :mod:`pyramid.config` that we use later. -#. Line 2 imports the ``Root`` class from :mod:`myproject.resources` that we - use later. - -#. Lines 4-12 define a function named ``main`` that returns a :app:`Pyramid` +#. Lines 3-16 define a function named ``main`` that returns a :app:`Pyramid` WSGI application. This function is meant to be called by the :term:`PasteDeploy` framework as a result of running ``pserve``. Within this function, application configuration is performed. - Lines 8-10 register a "default view" (a view that has no ``name`` - attribute). It is registered so that it will be found when the - :term:`context` of the request is an instance of the - :class:`myproject.resources.Root` class. The first argument to - ``add_view`` points at a Python function that does all the work for this - view, also known as a :term:`view callable`, via a :term:`dotted Python - name`. The view declaration also names a ``renderer``, which in this case - is a template that will be used to render the result of the view callable. - This particular view declaration points at - ``myproject:templates/mytemplate.pt``, which is a :term:`asset - specification` that specifies the ``mytemplate.pt`` file within the - ``templates`` directory of the ``myproject`` package. The template file - it actually points to is a :term:`Chameleon` ZPT template file. - - Line 11 registers a static view, which will serve up the files from the + Line 6 creates an instance of a :term:`Configurator`. + + Line 7 registers a static view, which will serve up the files from the ``mypackage:static`` :term:`asset specification` (the ``static`` directory of the ``mypackage`` package). - Line 12 returns a :term:`WSGI` application to the caller of the function + Line 8 adds a :term:`route` to the configuration. This route is later + used by a view in the ``views`` module. + + Line 9 calls ``config.scan()``, which picks up view registrations declared + elsewhere in the package (in this case, in the ``views.py`` module). + + Line 10 returns a :term:`WSGI` application to the caller of the function (Pyramid's pserve). .. index:: @@ -768,10 +745,22 @@ and which returns a :term:`response`. :language: python :linenos: -This bit of code was registered as the view callable within ``__init__.py`` -(via ``add_view``). ``add_view`` said that the default URL for instances -that are of the class :class:`myproject.resources.Root` should run this -:func:`myproject.views.my_view` function. +Lines 3-5 define and register a :term:`view callable` named ``my_view``. The +function named ``my_view`` is decorated with a ``view_config`` decorator +(which is processed by the ``config.scan()`` line in our ``__init__.py``). +The view_config decorator asserts that this view be found when a +:term:`route` named ``home`` is matched. In our case, because our +``__init__.py`` maps the route named ``home`` to the URL pattern ``/``, this +route will match when a visitor visits the root URL. The view_config +decorator also names a ``renderer``, which in this case is a template that +will be used to render the result of the view callable. This particular view +declaration points at ``templates/mytemplate.pt``, which is a :term:`asset +specification` that specifies the ``mytemplate.pt`` file within the +``templates`` directory of the ``myproject`` package. The asset +specification could have also been specified as +``myproject:templates/mytemplate.pt``; the leading package name and colon is +optional. The template file it actually points to is a :term:`Chameleon` ZPT +template file. This view callable function is handed a single piece of information: the :term:`request`. The *request* is an instance of the :term:`WebOb` @@ -781,8 +770,7 @@ This view returns a dictionary. When this view is invoked, a :term:`renderer` converts the dictionary returned by the view into HTML, and returns the result as the :term:`response`. This view is configured to invoke a renderer which uses a :term:`Chameleon` ZPT template -(``mypackage:templates/my_template.pt``, as specified in the ``__init__.py`` -file call to ``add_view``). +(``templates/my_template.pt``). See :ref:`views_which_use_a_renderer` for more information about how views, renderers, and templates relate and cooperate. @@ -798,35 +786,6 @@ renderers, and templates relate and cooperate. the speed at which templates may be rendered. .. index:: - single: resources.py - -.. _resourcespy_project_section: - -``resources.py`` -~~~~~~~~~~~~~~~~ - -The ``resources.py`` module provides the :term:`resource` data and behavior -for our application. Resources are objects which exist to provide site -structure in applications which use :term:`traversal` to map URLs to code. -We write a class named ``Root`` that provides the behavior for the root -resource. - -.. literalinclude:: MyProject/myproject/resources.py - :language: python - :linenos: - -#. Lines 1-3 define the Root class. The Root class is a "root resource - factory" function that will be called by the :app:`Pyramid` *Router* for - each request when it wants to find the root of the resource tree. - -In a "real" application, the Root object would likely not be such a simple -object. Instead, it might be an object that could access some persistent -data store, such as a database. :app:`Pyramid` doesn't make any assumption -about which sort of data storage you'll want to use, so the sample -application uses an instance of :class:`myproject.resources.Root` to -represent the root. - -.. index:: single: static directory ``static`` @@ -838,11 +797,11 @@ template. It includes CSS and images. ``templates/mytemplate.pt`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The single :term:`Chameleon` template that exists in the project. Its contents -are too long to show here, but it displays a default page when rendered. It -is referenced by the call to ``add_view`` as the ``renderer`` attribute in -the ``__init__`` file. See :ref:`views_which_use_a_renderer` for more -information about renderers. +The single :term:`Chameleon` template that exists in the project. Its +contents are too long to show here, but it displays a default page when +rendered. It is referenced by the call to ``@view_config`` as the +``renderer`` of the ``my_view`` view callable in the ``views.py`` file. See +:ref:`views_which_use_a_renderer` for more information about renderers. Templates are accessed and used by view configurations and sometimes by view functions themselves. See :ref:`templates_used_directly` and @@ -907,39 +866,12 @@ named ``views`` instead of within a single ``views.py`` file, you might: can be empty, this just tells Python that the ``views`` directory is a *package*. -Then change the __init__.py of your myproject project (*not* the -``__init__.py`` you just created in the ``views`` directory, the one in its -parent directory). For example, from something like: - -.. code-block:: python - :linenos: - - config.add_view('myproject.views.my_view', - renderer='myproject:templates/mytemplate.pt') - -To this: - -.. code-block:: python - :linenos: - - config.add_view('myproject.views.blog.my_view', - renderer='myproject:templates/mytemplate.pt') - -You can then continue to add files to the ``views`` directory, and refer to -view classes or functions within those files via the dotted name passed as -the first argument to ``add_view``. For example, if you added a file named -``anothermodule.py`` to the ``views`` subdirectory, and added a view callable -named ``my_view`` to it: - -.. code-block:: python - :linenos: - - config.add_view('myproject.views.anothermodule.my_view', - renderer='myproject:templates/anothertemplate.pt') - -This pattern can be used to rearrage code referred to by any Pyramid API -argument which accepts a :term:`dotted Python name` or direct object -reference. +You can then continue to add view callable functions to the ``blog.py`` +module, but you can also add other ``.py`` files which contain view callable +functions to the ``views`` directory. As long as you use the +``@view_config`` directive to register views in conjuction with +``config.scan()`` they will be picked up automatically when the application +is restarted. Using the Interactive Shell --------------------------- @@ -949,16 +881,39 @@ configuration as would be loaded if you were running your Pyramid application via ``pserve``. This can be a useful debugging tool. See :ref:`interactive_shell` for more details. +.. _alternate_wsgi_server: + Using an Alternate WSGI Server ------------------------------ -The code generated by a :app:`Pyramid` scaffold assumes that you will be +The code generated by :app:`Pyramid` scaffolding assumes that you will be using the ``pserve`` command to start your application while you do -development. However, ``pserve`` is by no means the only way to start up and -serve a :app:`Pyramid` application. As we saw in :ref:`firstapp_chapter`, -``pserve`` needn't be invoked at all to run a :app:`Pyramid` application. -The use of ``pserve`` to run a :app:`Pyramid` application is purely -conventional based on the output of its scaffold. +development. The default rendering of Pyramid scaffolding uses the *wsgiref* +WSGI server, which is a server that is ill-suited for production usage: its +main feature is that it works on all platforms and all systems, making it a +good choice as a default server from the perspective of Pyramid's developers. + +To use a server more suitable for production, you have a number of choices. +Replace the ``use = egg:pyramid#wsgref`` line in your ``production.ini`` with +one of the following. + +``use = egg:Paste#http`` + + ``paste.httpserver`` is Windows, UNIX, and Python 2 compatible. You'll + need to ``easy_install Paste`` into your Pyramid virtualenv for this server + to work. + +``use = egg:pyramid#cherrypy`` + + The ``CherryPy`` WSGI server is Windows, UNIX, Python 2, and Python 3 + compatible. You'll need to ``easy_install CherryPy`` into your Pyramid + virtualenv for this server to work. + +``pserve`` is by no means the only way to start up and serve a :app:`Pyramid` +application. As we saw in :ref:`firstapp_chapter`, ``pserve`` needn't be +invoked at all to run a :app:`Pyramid` application. The use of ``pserve`` to +run a :app:`Pyramid` application is purely conventional based on the output +of its scaffold. Any :term:`WSGI` server is capable of running a :app:`Pyramid` application. Some WSGI servers don't require the :term:`PasteDeploy` framework's diff --git a/docs/narr/router.rst b/docs/narr/router.rst index d08261b17..b78362066 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -9,74 +9,67 @@ Request Processing ================== -Once a :app:`Pyramid` application is up and running, it is ready to -accept requests and return responses. +Once a :app:`Pyramid` application is up and running, it is ready to accept +requests and return responses. What happens from the time a :term:`WSGI` +request enters a :app:`Pyramid` application through to the point that +:app:`Pyramid` hands off a response back to WSGI for upstream processing? -What happens from the time a :term:`WSGI` request enters a -:app:`Pyramid` application through to the point that -:app:`Pyramid` hands off a response back to WSGI for upstream -processing? +#. A user initiates a request from his browser to the hostname and port + number of the WSGI server used by the :app:`Pyramid` application. -#. A user initiates a request from his browser to the hostname and - port number of the WSGI server used by the :app:`Pyramid` - application. - -#. The WSGI server used by the :app:`Pyramid` application passes - the WSGI environment to the ``__call__`` method of the - :app:`Pyramid` :term:`router` object. +#. The WSGI server used by the :app:`Pyramid` application passes the WSGI + environment to the ``__call__`` method of the :app:`Pyramid` + :term:`router` object. #. A :term:`request` object is created based on the WSGI environment. -#. The :term:`application registry` and the :term:`request` object - created in the last step are pushed on to the :term:`thread local` - stack that :app:`Pyramid` uses to allow the functions named +#. The :term:`application registry` and the :term:`request` object created in + the last step are pushed on to the :term:`thread local` stack that + :app:`Pyramid` uses to allow the functions named :func:`~pyramid.threadlocal.get_current_request` and :func:`~pyramid.threadlocal.get_current_registry` to work. #. A :class:`~pyramid.events.NewRequest` :term:`event` is sent to any subscribers. -#. If any :term:`route` has been defined within application - configuration, the :app:`Pyramid` :term:`router` calls a - :term:`URL dispatch` "route mapper." The job of the mapper is to - examine the request to determine whether any user-defined - :term:`route` matches the current WSGI environment. The - :term:`router` passes the request as an argument to the mapper. - -#. If any route matches, the request is mutated; a ``matchdict`` and - ``matched_route`` attributes are added to the request object; the - former contains a dictionary representing the matched dynamic - elements of the request's ``PATH_INFO`` value, the latter contains - the :class:`~pyramid.interfaces.IRoute` object representing the - route which matched. The root object associated with the route - found is also generated: if the :term:`route configuration` which - matched has an associated a ``factory`` argument, this factory is - used to generate the root object, otherwise a default :term:`root - factory` is used. - -#. If a route match was *not* found, and a ``root_factory`` argument - was passed to the :term:`Configurator` constructor, that callable - is used to generate the root object. If the ``root_factory`` - argument passed to the Configurator constructor was ``None``, a - default root factory is used to generate a root object. - -#. The :app:`Pyramid` router calls a "traverser" function with the - root object and the request. The traverser function attempts to - traverse the root object (using any existing ``__getitem__`` on the - root object and subobjects) to find a :term:`context`. If the root - object has no ``__getitem__`` method, the root itself is assumed to - be the context. The exact traversal algorithm is described in - :ref:`traversal_chapter`. The traverser function returns a - dictionary, which contains a :term:`context` and a :term:`view - name` as well as other ancillary information. - -#. The request is decorated with various names returned from the - traverser (such as ``context``, ``view_name``, and so forth), so - they can be accessed via e.g. ``request.context`` within - :term:`view` code. - -#. A :class:`~pyramid.events.ContextFound` :term:`event` is - sent to any subscribers. +#. If any :term:`route` has been defined within application configuration, + the :app:`Pyramid` :term:`router` calls a :term:`URL dispatch` "route + mapper." The job of the mapper is to examine the request to determine + whether any user-defined :term:`route` matches the current WSGI + environment. The :term:`router` passes the request as an argument to the + mapper. + +#. If any route matches, the route mapper adds attributes to the request: + ``matchdict`` and ``matched_route`` attributes are added to the request + object. The former contains a dictionary representing the matched dynamic + elements of the request's ``PATH_INFO`` value, the latter contains the + :class:`~pyramid.interfaces.IRoute` object representing the route which + matched. The root object associated with the route found is also + generated: if the :term:`route configuration` which matched has an + associated a ``factory`` argument, this factory is used to generate the + root object, otherwise a default :term:`root factory` is used. + +#. If a route match was *not* found, and a ``root_factory`` argument was + passed to the :term:`Configurator` constructor, that callable is used to + generate the root object. If the ``root_factory`` argument passed to the + Configurator constructor was ``None``, a default root factory is used to + generate a root object. + +#. The :app:`Pyramid` router calls a "traverser" function with the root + object and the request. The traverser function attempts to traverse the + root object (using any existing ``__getitem__`` on the root object and + subobjects) to find a :term:`context`. If the root object has no + ``__getitem__`` method, the root itself is assumed to be the context. The + exact traversal algorithm is described in :ref:`traversal_chapter`. The + traverser function returns a dictionary, which contains a :term:`context` + and a :term:`view name` as well as other ancillary information. + +#. The request is decorated with various names returned from the traverser + (such as ``context``, ``view_name``, and so forth), so they can be + accessed via e.g. ``request.context`` within :term:`view` code. + +#. A :class:`~pyramid.events.ContextFound` :term:`event` is sent to any + subscribers. #. :app:`Pyramid` looks up a :term:`view` callable using the context, the request, and the view name. If a view callable doesn't exist for this @@ -86,20 +79,17 @@ processing? :class:`~pyramid.httpexceptions.HTTPNotFound` exception, which is meant to be caught by a surrounding :term:`exception view`. -#. If a view callable was found, :app:`Pyramid` attempts to call - the view function. - -#. If an :term:`authorization policy` is in use, and the view was protected - by a :term:`permission`, :app:`Pyramid` passes the context, the request, - and the view_name to a function which determines whether the view being - asked for can be executed by the requesting user, based on credential - information in the request and security information attached to the - context. If it returns ``True``, :app:`Pyramid` calls the view callable - to obtain a response. If it returns ``False``, it raises a - :class:`~pyramid.httpexceptions.HTTPForbidden` exception, which is meant - to be called by a surrounding :term:`exception view`. - -#. If any exception was raised within a :term:`root factory`, by +#. If a view callable was found, :app:`Pyramid` attempts to call it. If an + :term:`authorization policy` is in use, and the view configuration is + protected by a :term:`permission`, :app:`Pyramid` determines whether the + view callable being asked for can be executed by the requesting user based + on credential information in the request and security information attached + to the context. If the view execution is allowed, :app:`Pyramid` calls + the view callable to obtain a response. If view execution is forbidden, + :app:`Pyramid` raises a :class:`~pyramid.httpexceptions.HTTPForbidden` + exception. + +#. If any exception is raised within a :term:`root factory`, by :term:`traversal`, by a :term:`view callable` or by :app:`Pyramid` itself (such as when it raises :class:`~pyramid.httpexceptions.HTTPNotFound` or :class:`~pyramid.httpexceptions.HTTPForbidden`), the router catches the @@ -128,9 +118,8 @@ processing? .. image:: router.png -This is a very high-level overview that leaves out various details. -For more detail about subsystems invoked by the :app:`Pyramid` router -such as traversal, URL dispatch, views, and event processing, see -:ref:`urldispatch_chapter`, :ref:`views_chapter`, and -:ref:`events_chapter`. +This is a very high-level overview that leaves out various details. For more +detail about subsystems invoked by the :app:`Pyramid` router such as +traversal, URL dispatch, views, and event processing, see +:ref:`urldispatch_chapter`, :ref:`views_chapter`, and :ref:`events_chapter`. diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index f4ebef154..a7fc5d33c 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -24,11 +24,11 @@ The Startup Process ------------------- The easiest and best-documented way to start and serve a :app:`Pyramid` -application is to use the ``pserve`` command against a -:term:`PasteDeploy` ``.ini`` file. This uses the ``.ini`` file to infer -settings and starts a server listening on a port. For the purposes of this -discussion, we'll assume that you are using this command to run your -:app:`Pyramid` application. +application is to use the ``pserve`` command against a :term:`PasteDeploy` +``.ini`` file. This uses the ``.ini`` file to infer settings and starts a +server listening on a port. For the purposes of this discussion, we'll +assume that you are using this command to run your :app:`Pyramid` +application. Here's a high-level time-ordered overview of what happens when you press ``return`` after running ``pserve development.ini``. @@ -56,11 +56,12 @@ Here's a high-level time-ordered overview of what happens when you press #. The framework finds all :mod:`logging` related configuration in the ``.ini`` file and uses it to configure the Python standard library logging - system for this application. + system for this application. See :ref:`logging_config` for more + information. -#. The application's *constructor* (named by the entry point reference or +#. The application's *constructor* named by the entry point reference or dotted Python name on the ``use=`` line of the section representing your - :app:`Pyramid` application) is passed the key/value parameters mentioned + :app:`Pyramid` application is passed the key/value parameters mentioned within the section in which it's defined. The constructor is meant to return a :term:`router` instance, which is a :term:`WSGI` application. @@ -76,12 +77,14 @@ Here's a high-level time-ordered overview of what happens when you press Note that the constructor function accepts a ``global_config`` argument, which is a dictionary of key/value pairs mentioned in the ``[DEFAULT]`` - section of an ``.ini`` file. It also accepts a ``**settings`` argument, - which collects another set of arbitrary key/value pairs. The arbitrary - key/value pairs received by this function in ``**settings`` will be - composed of all the key/value pairs that are present in the ``[app:main]`` - section (except for the ``use=`` setting) when this function is called by - when you run ``pserve``. + section of an ``.ini`` file (if `[DEFAULT] + <http://docs.pylonsproject.org/projects/pyramid/dev/narr/paste.html#defaults-section-of-a-pastedeploy-ini-file>`__ + is present). It also accepts a ``**settings`` argument, which collects + another set of arbitrary key/value pairs. The arbitrary key/value pairs + received by this function in ``**settings`` will be composed of all the + key/value pairs that are present in the ``[app:main]`` section (except for + the ``use=`` setting) when this function is called by when you run + ``pserve``. Our generated ``development.ini`` file looks like so: @@ -95,7 +98,8 @@ Here's a high-level time-ordered overview of what happens when you press 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'}``. + 'pyramid.default_locale_name':'en'}``. See :ref:`environment_chapter` for + the meanings of these keys. #. The ``main`` function first constructs a :class:`~pyramid.config.Configurator` instance, passing a root resource @@ -103,10 +107,6 @@ Here's a high-level time-ordered overview of what happens when you press ``settings`` dictionary captured via the ``**settings`` kwarg as its ``settings`` argument. - The root resource factory is invoked on every request to retrieve the - application's root resource. It is not called during startup, only when a - request is handled. - The ``settings`` dictionary contains all the options in the ``[app:main]`` section of our .ini file except the ``use`` option (which is internal to PasteDeploy) such as ``pyramid.reload_templates``, diff --git a/docs/narr/tb_introspector.png b/docs/narr/tb_introspector.png Binary files differnew file mode 100644 index 000000000..4ae406a86 --- /dev/null +++ b/docs/narr/tb_introspector.png diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index fb9dd56c2..11318d9eb 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -403,14 +403,6 @@ The language definition documentation for Chameleon ZPT-style templates is available from `the Chameleon website <http://chameleon.repoze.org/>`_. -.. warning:: - - :term:`Chameleon` only works on :term:`CPython` platforms and - :term:`Google App Engine`. On :term:`Jython` and other non-CPython - platforms, you should use Mako (see :ref:`mako_templates`) or - ``pyramid_jinja2`` instead. See - :ref:`available_template_system_bindings`. - Given a :term:`Chameleon` ZPT template named ``foo.pt`` in a directory in your application named ``templates``, you can render the template as a :term:`renderer` like so: diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index 05e851fde..7ee432fa7 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -52,7 +52,7 @@ The suggested mechanism for unit and integration testing of a :app:`Pyramid` application is the Python :mod:`unittest` module. Although this module is named :mod:`unittest`, it is actually capable of driving both unit and integration tests. A good :mod:`unittest` tutorial is available within `Dive -Into Python <http://diveintopython.org/unit_testing/index.html>`_ by Mark +Into Python <http://diveintopython.nfshost.com/unit_testing/index.html>`_ by Mark Pilgrim. :app:`Pyramid` provides a number of facilities that make unit, integration, diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 9ceb20f21..35613ea1b 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -65,7 +65,7 @@ example: config.add_view(myview, route_name='myroute') When a :term:`view callable` added to the configuration by way of -:meth:`~pyramid.config.Configurator.add_view` bcomes associated with a route +:meth:`~pyramid.config.Configurator.add_view` becomes associated with a route via its ``route_name`` predicate, that view callable will always be found and invoked when the associated route pattern matches during a request. @@ -101,7 +101,7 @@ that references ``myroute`` as a ``route_name`` parameter: def myview(request): return Response('OK') -THe above combination of ``add_route`` and ``scan`` is completely equivalent +The above combination of ``add_route`` and ``scan`` is completely equivalent to using the previous combination of ``add_route`` and ``add_view``. .. index:: diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index af5d7f242..03000629c 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -621,6 +621,7 @@ against the ``amethod`` method could be spelled equivalently as the below: def amethod(self): return Response('hello') + .. index:: single: add_view @@ -658,6 +659,186 @@ configurations, you don't need to issue a :term:`scan` in order for the view configuration to take effect. .. index:: + single: view_defaults class decorator + +.. _view_defaults: + +``@view_defaults`` Class Decorator +---------------------------------- + +.. note:: + + This feature is new in Pyramid 1.3. + +If you use a class as a view, you can use the +:class:`pyramid.view.view_defaults` class decorator on the class to provide +defaults to the view configuration information used by every ``@view_config`` +decorator that decorates a method of that class. + +For instance, if you've got a class that has methods that represent "REST +actions", all which are mapped to the same route, but different request +methods, instead of this: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + from pyramid.response import Response + + class RESTView(object): + def __init__(self, request): + self.request = request + + @view_config(route_name='rest', request_method='GET') + def get(self): + return Response('get') + + @view_config(route_name='rest', request_method='POST') + def post(self): + return Response('post') + + @view_config(route_name='rest', request_method='DELETE') + def delete(self): + return Response('delete') + +You can do this: + +.. code-block:: python + :linenos: + + from pyramid.view import view_defaults + from pyramid.view import view_config + from pyramid.response import Response + + @view_defaults(route_name='rest') + class RESTView(object): + def __init__(self, request): + self.request = request + + @view_config(request_method='GET') + def get(self): + return Response('get') + + @view_config(request_method='POST') + def post(self): + return Response('post') + + @view_config(request_method='DELETE') + def delete(self): + return Response('delete') + +In the above example, we were able to take the ``route_name='rest'`` argument +out of the call to each individual ``@view_config`` statement, because we +used a ``@view_defaults`` class decorator to provide the argument as a +default to each view method it possessed. + +Arguments passed to ``@view_config`` will override any default passed to +``@view_defaults``. + +The ``view_defaults`` class decorator can also provide defaults to the +:meth:`pyramid.config.Configurator.add_view` directive when a decorated class +is passed to that directive as its ``view`` argument. For example, instead +of this: + +.. code-block:: python + :linenos: + + from pyramid.response import Response + from pyramid.config import Configurator + + class RESTView(object): + def __init__(self, request): + self.request = request + + def get(self): + return Response('get') + + def post(self): + return Response('post') + + def delete(self): + return Response('delete') + + if __name__ == '__main__': + config = Configurator() + config.add_route('rest', '/rest') + config.add_view( + RESTView, route_name='rest', attr='get', request_method='GET') + config.add_view( + RESTView, route_name='rest', attr='post', request_method='POST') + config.add_view( + RESTView, route_name='rest', attr='delete', request_method='DELETE') + +To reduce the amount of repetion in the ``config.add_view`` statements, we +can move the ``route_name='rest'`` argument to a ``@view_default`` class +decorator on the RESTView class: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + from pyramid.response import Response + from pyramid.config import Configurator + + @view_defaults(route_name='rest') + class RESTView(object): + def __init__(self, request): + self.request = request + + def get(self): + return Response('get') + + def post(self): + return Response('post') + + def delete(self): + return Response('delete') + + if __name__ == '__main__': + config = Configurator() + config.add_route('rest', '/rest') + config.add_view(RESTView, attr='get', request_method='GET') + config.add_view(RESTView, attr='post', request_method='POST') + config.add_view(RESTView, attr='delete', request_method='DELETE') + +:class:`pyramid.view.view_defaults` accepts the same set of arguments that +:class:`pyramid.view.view_config` does, and they have the same meaning. Each +argument passed to ``view_defaults`` provides a default for the view +configurations of methods of the class it's decorating. + +Normal Python inheritance rules apply to defaults added via +``view_defaults``. For example: + +.. code-block:: python + :linenos: + + @view_defaults(route_name='rest') + class Foo(object): + pass + + class Bar(Foo): + pass + +The ``Bar`` class above will inherit its view defaults from the arguments +passed to the ``view_defaults`` decorator of the ``Foo`` class. To prevent +this from happening, use a ``view_defaults`` decorator without any arguments +on the subclass: + +.. code-block:: python + :linenos: + + @view_defaults(route_name='rest') + class Foo(object): + pass + + @view_defaults() + class Bar(Foo): + pass + +The ``view_defaults`` decorator only works as a class decorator; using it +against a function or a method will produce nonsensical results. + +.. index:: single: view security pair: security; view |
