From fb6a5ce52a275f7798e82a34b5907ea118cbd2ff Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 18 Dec 2010 02:27:14 -0500 Subject: model -> resource; resource -> asset --- docs/index.rst | 4 +- docs/latexindex.rst | 4 +- docs/narr/assets.rst | 198 +++++++++++++++ docs/narr/contextfinding.rst | 18 +- docs/narr/declarative.rst | 101 ++++++-- docs/narr/extending.rst | 10 +- docs/narr/hooks.rst | 42 ++-- docs/narr/hybrid.rst | 51 ++-- docs/narr/introduction.rst | 24 +- docs/narr/modelgraphtraverser.png | Bin 232233 -> 0 bytes docs/narr/models.rst | 331 ------------------------- docs/narr/project.rst | 8 +- docs/narr/resources.rst | 465 ++++++++++++++++++++---------------- docs/narr/resourcetreetraverser.png | Bin 0 -> 232233 bytes docs/narr/security.rst | 67 +++--- docs/narr/static.rst | 197 ++++++++------- docs/narr/templates.rst | 29 ++- docs/narr/threadlocals.rst | 8 +- docs/narr/traversal.rst | 199 ++++++++------- docs/narr/unittesting.rst | 37 ++- docs/narr/urldispatch.rst | 12 +- docs/narr/vhosting.rst | 28 +-- docs/narr/views.rst | 289 +++++++++++----------- docs/narr/zca.rst | 2 +- pyramid/config.py | 18 +- pyramid/includes/meta.zcml | 8 +- pyramid/request.py | 36 +-- pyramid/traversal.py | 245 ++++++++++--------- pyramid/url.py | 55 +++-- pyramid/zcml.py | 19 +- 30 files changed, 1235 insertions(+), 1270 deletions(-) create mode 100644 docs/narr/assets.rst delete mode 100644 docs/narr/modelgraphtraverser.png delete mode 100644 docs/narr/models.rst create mode 100644 docs/narr/resourcetreetraverser.png diff --git a/docs/index.rst b/docs/index.rst index 073a562ca..3add95117 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -48,7 +48,7 @@ Narrative documentation in chapter form explaining how to use narr/webob narr/sessions narr/templates - narr/models + narr/resources narr/security narr/i18n narr/vhosting @@ -58,7 +58,7 @@ Narrative documentation in chapter form explaining how to use narr/hooks narr/declarative narr/extending - narr/resources + narr/assets narr/router narr/threadlocals narr/zca diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 0ef67248b..81788035a 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -41,7 +41,7 @@ Narrative Documentation narr/webob narr/sessions narr/templates - narr/models + narr/resources narr/security narr/i18n narr/vhosting @@ -51,7 +51,7 @@ Narrative Documentation narr/hooks narr/declarative narr/extending - narr/resources + narr/assets narr/router narr/startup narr/threadlocals diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst new file mode 100644 index 000000000..1932e19ff --- /dev/null +++ b/docs/narr/assets.rst @@ -0,0 +1,198 @@ +.. index:: + single: assets + +.. _assets_chapter: + +Assets +====== + +An :term:`asset` is any file contained within a Python :term:`package` which +is *not* a Python source code file. For example, each of the following is an +asset: + +- a :term:`Chameleon` template file contained within a Python package. + +- a GIF image file contained within a Python package. + +- a CSS file contained within a Python package. + +- a JavaScript source file contained within a Python package. + +- A directory within a package that does not have an ``__init__.py`` + in it (if it possessed an ``__init__.py`` it would *be* a package). + +The use of assets is quite common in most web development projects. For +example, when you create a :app:`Pyramid` application using one of the +available "paster" templates, as described in :ref:`creating_a_project`, the +directory representing the application contains a Python :term:`package`. +Within that Python package, there are directories full of files which are +assets. For example, there is a ``templates`` directory which contains +``.pt`` files, and a ``static`` directory which contains ``.css``, ``.js``, +and ``.gif`` files. + +.. _understanding_assets: + +Understanding Assets +-------------------- + +Let's imagine you've created a :app:`Pyramid` application that uses a +:term:`Chameleon` ZPT template via the +:func:`pyramid.chameleon_zpt.render_template_to_response` API. For example, +the application might address the asset named ``templates/some_template.pt`` +using that API within a ``views.py`` file inside a ``myapp`` package: + +.. ignore-next-block +.. code-block:: python + :linenos: + + from pyramid.chameleon_zpt import render_template_to_response + render_template_to_response('templates/some_template.pt') + +"Under the hood", when this API is called, :app:`Pyramid` attempts +to make sense out of the string ``templates/some_template.pt`` +provided by the developer. To do so, it first finds the "current" +package. The "current" package is the Python package in which the +``views.py`` module which contains this code lives. This would be the +``myapp`` package, according to our example so far. By resolving the +current package, :app:`Pyramid` has enough information to locate +the actual template file. These are the elements it needs: + +- The *package name* (``myapp``) + +- The *asset name* (``templates/some_template.pt``) + +:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the package name +and asset name to an absolute (operating-system-specific) file name. It +eventually passes this resolved absolute filesystem path to the Chameleon +templating engine, which then uses it to load, parse, and execute the +template file. + +Package names often contain dots. For example, ``pyramid`` is a package. +Asset names usually look a lot like relative UNIX file paths. + +.. index:: + pair: overriding; assets + +.. _overriding_assets_section: + +Overriding Assets +----------------- + +It can often be useful to override specific assets from "outside" a given +:app:`Pyramid` application. For example, you may wish to reuse an existing +:app:`Pyramid` application more or less unchanged. However, some specific +template file owned by the application might have inappropriate HTML, or some +static asset (such as a logo file or some CSS file) might not be appropriate. +You *could* just fork the application entirely, but it's often more +convenient to just override the assets that are inappropriate and reuse the +application "as is". This is particularly true when you reuse some "core" +application over and over again for some set of customers (such as a CMS +application, or some bug tracking application), and you want to make +arbitrary visual modifications to a particular application deployment without +forking the underlying code. + +To this end, :app:`Pyramid` contains a feature that makes it possible to +"override" one asset with one or more other assets. In support of this +feature, a :term:`Configurator` API exists named +:meth:`pyramid.config.Configurator.override_asset`. This API allows you to +*override* the following kinds of assets defined in any Python package: + +- Individual :term:`Chameleon` templates. + +- A directory containing multiple Chameleon templates. + +- Individual static files served up by an instance of the + ``pyramid.view.static`` helper class. + +- A directory of static files served up by an instance of the + ``pyramid.view.static`` helper class. + +- Any other asset (or set of assets) addressed by code that uses the + setuptools :term:`pkg_resources` API. + +.. note:: The :term:`ZCML` directive named ``asset`` serves the same purpose + as the :meth:`pyramid.config.Configurator.override_asset` method. + +.. index:: + single: override_asset + +.. _override_asset: + +The ``override_asset`` API +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An individual call to :meth:`pyramid.config.Configurator.override_asset` +can override a single asset. For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset( + to_override='some.package:templates/mytemplate.pt', + override_with='another.package:othertemplates/anothertemplate.pt') + +The string value passed to both ``to_override`` and ``override_with`` sent to +the ``override_asset`` API is called an :term:`asset specification`. The +colon separator in a specification separates the *package name* from the +*asset name*. The colon and the following asset name are optional. If they +are not specified, the override attempts to resolve every lookup into a +package from the directory of another package. For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='some.package', + override_with='another.package') + +Individual subdirectories within a package can also be overridden: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='some.package:templates/', + override_with='another.package:othertemplates/') + + +If you wish to override a directory with another directory, you *must* +make sure to attach the slash to the end of both the ``to_override`` +specification and the ``override_with`` specification. If you fail to +attach a slash to the end of a specification that points to a directory, +you will get unexpected results. + +You cannot override a directory specification with a file specification, and +vice versa: a startup error will occur if you try. You cannot override an +asset with itself: a startup error will occur if you try. + +Only individual *package* assets may be overridden. Overrides will not +traverse through subpackages within an overridden package. This means that +if you want to override assets for both ``some.package:templates``, and +``some.package.views:templates``, you will need to register two overrides. + +The package name in a specification may start with a dot, meaning that +the package is relative to the package in which the configuration +construction file resides (or the ``package`` argument to the +:class:`pyramid.config.Configurator` class construction). +For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='.subpackage:templates/', + override_with='another.package:templates/') + +Multiple calls to ``override_asset`` which name a shared ``to_override`` but +a different ``override_with`` specification can be "stacked" to form a search +path. The first asset that exists in the search path will be used; if no +asset exists in the override path, the original asset is used. + +Asset overrides can actually override assets other than templates and static +files. Any software which uses the +:func:`pkg_resources.get_resource_filename`, +:func:`pkg_resources.get_resource_stream` or +:func:`pkg_resources.get_resource_string` APIs will obtain an overridden file +when an override is used. + diff --git a/docs/narr/contextfinding.rst b/docs/narr/contextfinding.rst index 691ad7b8e..ad0d1f9da 100644 --- a/docs/narr/contextfinding.rst +++ b/docs/narr/contextfinding.rst @@ -95,20 +95,20 @@ don't deal very well with URLs that represent arbitrary-depth hierarchies. But :term:`traversal` *does* work well for URLs that represent -arbitrary-depth hierarchies. Since the path segments that compose a -URL are addressed separately, it becomes very easy to form URLs that -represent arbitrary depth hierarchies in a system that uses traversal. -When you're willing to treat your application models as a graph that -can be traversed, it also becomes easy to provide "instance-level -security": you just attach a security declaration to each instance in -the graph. This is not nearly as easy to do when using URL dispatch. +arbitrary-depth hierarchies. Since the path segments that compose a URL are +addressed separately, it becomes very easy to form URLs that represent +arbitrary depth hierarchies in a system that uses traversal. When you're +willing to treat your application resources as a tree that can be traversed, +it also becomes easy to provide "instance-level security": you just attach a +security declaration to each instance in the tree. This is not nearly as +easy to do when using URL dispatch. In essence, the choice to use traversal vs. URL dispatch is largely religious. Traversal dispatch probably just doesn't make any sense when you possess completely "square" data stored in a relational database because it requires the construction and maintenance of a -graph and requires that the developer think about mapping URLs to code -in terms of traversing that graph. However, when you have a +tree and requires that the developer think about mapping URLs to code +in terms of traversing that tree. However, when you have a hierarchical data store, using traversal can provide significant advantages over using URL-based dispatch. diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst index 99c5a75ac..54e9ecaa1 100644 --- a/docs/narr/declarative.rst +++ b/docs/narr/declarative.rst @@ -557,7 +557,7 @@ example of a view declaration in ZCML is as follows: :linenos: @@ -566,7 +566,7 @@ The above maps the ``.views.hello_world`` view callable function to the following set of :term:`context finding` results: - A :term:`context` object which is an instance (or subclass) of the - Python class represented by ``.models.Hello`` + Python class represented by ``.resources.Hello`` - A :term:`view name` equalling ``hello.html``. @@ -575,28 +575,27 @@ the following set of :term:`context finding` results: above) mean "relative to the Python package directory in which this :term:`ZCML` file is stored". So if the above ``view`` declaration was made inside a ``configure.zcml`` file that lived in the - ``hello`` package, you could replace the relative ``.models.Hello`` - with the absolute ``hello.models.Hello``; likewise you could + ``hello`` package, you could replace the relative ``.resources.Hello`` + with the absolute ``hello.resources.Hello``; likewise you could replace the relative ``.views.hello_world`` with the absolute ``hello.views.hello_world``. Either the relative or absolute form is functionally equivalent. It's often useful to use the relative form, in case your package's name changes. It's also shorter to type. -You can also declare a *default view callable* for a :term:`model` -type: +You can also declare a *default view callable* for a :term:`resource` type: .. code-block:: xml :linenos: A *default view callable* simply has no ``name`` attribute. For the above registration, when a :term:`context` is found that is of the -type ``.models.Hello`` and there is no :term:`view name` associated +type ``.resources.Hello`` and there is no :term:`view name` associated with the result of :term:`context finding`, the *default view callable* will be used. In this case, it's the view at ``.views.hello_world``. @@ -608,7 +607,7 @@ string as its ``name`` attribute: :linenos: @@ -823,6 +822,75 @@ an imperative equivalent to the ``static`` ZCML directive. Use of the to using ZCML for the same purpose. See :ref:`static_resources_section` for more information. +.. index:: + pair: ZCML directive; asset + +.. _asset_zcml_directive: + +The ``asset`` ZCML Directive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of using :meth:`pyramid.config.Configurator.override_asset` during +:term:`imperative configuration`, an equivalent ZCML directive can be used. +The ZCML ``asset`` tag is a frontend to using +:meth:`pyramid.config.Configurator.override_asset`. + +An individual :app:`Pyramid` ``asset`` ZCML statement can override a +single asset. For example: + +.. code-block:: xml + :linenos: + + + +The string value passed to both ``to_override`` and ``override_with`` +attached to an ``asset`` directive is called an "asset specification". The +colon separator in a specification separates the *package name* from the +*asset name*. The colon and the following asset name are optional. If they +are not specified, the override attempts to resolve every lookup into a +package from the directory of another package. For example: + +.. code-block:: xml + :linenos: + + + +Individual subdirectories within a package can also be overridden: + +.. code-block:: xml + :linenos: + + + +If you wish to override an asset directory with another directory, you *must* +make sure to attach the slash to the end of both the ``to_override`` +specification and the ``override_with`` specification. If you fail to attach +a slash to the end of an asset specification that points to a directory, you +will get unexpected results. + +The package name in an asset specification may start with a dot, meaning that +the package is relative to the package in which the ZCML file resides. For +example: + +.. code-block:: xml + :linenos: + + + +See also :ref:`asset_directive`. + .. _zcml_authorization_policy: Enabling an Authorization Policy Via ZCML @@ -833,12 +901,11 @@ than imperative configuration, modify the ZCML file loaded by your application (usually named ``configure.zcml``) to enable an authorization policy. -For example, to enable a policy which compares the value of an "auth -ticket" cookie passed in the request's environment which contains a -reference to a single :term:`principal` against the principals present -in any :term:`ACL` found in model data when attempting to call some -:term:`view`, modify your ``configure.zcml`` to look something like -this: +For example, to enable a policy which compares the value of an "auth ticket" +cookie passed in the request's environment which contains a reference to a +single :term:`principal` against the principals present in any :term:`ACL` +found in the resource tree when attempting to call some :term:`view`, modify +your ``configure.zcml`` to look something like this: .. code-block:: xml :linenos: @@ -958,7 +1025,7 @@ Built-In Authorization Policy ZCML Directives ``aclauthorizationpolicy`` When this directive is used, authorization information is obtained -from :term:`ACL` objects attached to model instances. +from :term:`ACL` objects attached to resources. An example of its usage, with all attributes fully expanded: @@ -1069,7 +1136,6 @@ with ``.jinja2`` as its ``renderer`` value. The ``name`` passed to the See also :ref:`renderer_directive` and :meth:`pyramid.config.Configurator.add_renderer`. - Overriding an Existing Renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1207,4 +1273,3 @@ See also :ref:`subscriber_directive` and :ref:`events_chapter`. .. - hooks chapter still has topics for ZCML -.. - resources chapter still has topics for ZCML diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 1f9fd1288..9802a01f6 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -191,15 +191,15 @@ the original application with slight tweaks. For example: :linenos: -A similar pattern can be used to *extend* the application with -```` declarations. Just register a new view against some -existing model type and make sure the URLs it implies are available on -some other page rendering. +A similar pattern can be used to *extend* the application with ```` +declarations. Just register a new view against some existing resource type +(using ``context``) and make sure the URLs it implies are available on some +other page rendering. .. index:: pair: overriding; routes diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 10f463a17..006f5d5cb 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -88,11 +88,10 @@ callable: it is false. .. warning:: When a NotFound view callable accepts an argument list as - described in :ref:`request_and_context_view_definitions`, the - ``context`` passed as the first argument to the view callable will - be the :exc:`pyramid.exceptions.NotFound` exception instance. - If available, the *model* context will still be available as - ``request.context``. + described in :ref:`request_and_context_view_definitions`, the ``context`` + passed as the first argument to the view callable will be the + :exc:`pyramid.exceptions.NotFound` exception instance. If available, the + resource context will still be available as ``request.context``. .. index:: single: forbidden view @@ -221,14 +220,14 @@ a class that implements the following interface: """ Return a dictionary with (at least) the keys ``root``, ``context``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. These values are - typically the result of an object graph traversal. ``root`` - is the physical root object, ``context`` will be a model + typically the result of a resource tree traversal. ``root`` + is the physical root object, ``context`` will be a resource object, ``view_name`` will be the view name used (a Unicode name), ``subpath`` will be a sequence of Unicode names that followed the view name but were not traversed, ``traversed`` will be a sequence of Unicode names that were traversed (including the virtual root path, if any) ``virtual_root`` - will be a model object representing the virtual root (or the + will be a resource object representing the virtual root (or the physical root if traversal was not performed), and ``virtual_root_path`` will be a sequence representing the virtual root path (a sequence of Unicode names) or None if @@ -255,30 +254,29 @@ traverser would be used. For example: If the above stanza was added to a ``configure.zcml`` file, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only when the application :term:`root factory` returned an instance of the -``myapp.models.MyRoot`` object. Otherwise it would use the default +``myapp.resources.MyRoot`` object. Otherwise it would use the default :app:`Pyramid` traverser to do traversal. .. index:: single: url generator -Changing How :mod:`pyramid.url.model_url` Generates a URL +Changing How :mod:`pyramid.url.resource_url` Generates a URL ------------------------------------------------------------ -When you add a traverser as described in -:ref:`changing_the_traverser`, it's often convenient to continue to -use the :func:`pyramid.url.model_url` API. However, since the way -traversal is done will have been modified, the URLs it generates by -default may be incorrect. +When you add a traverser as described in :ref:`changing_the_traverser`, it's +often convenient to continue to use the :func:`pyramid.url.resource_url` API. +However, since the way traversal is done will have been modified, the URLs it +generates by default may be incorrect. If you've added a traverser, you can change how -:func:`pyramid.url.model_url` generates a URL for a specific type -of :term:`context` by adding an adapter stanza for +:func:`pyramid.url.resource_url` generates a URL for a specific type of +resource by adding an adapter stanza for :class:`pyramid.interfaces.IContextURL` to your application's ``configure.zcml``: @@ -288,13 +286,13 @@ of :term:`context` by adding an adapter stanza for In the above example, the ``myapp.traversal.URLGenerator`` class will -be used to provide services to :func:`pyramid.url.model_url` any -time the :term:`context` passed to ``model_url`` is of class -``myapp.models.MyRoot``. The asterisk following represents the type +be used to provide services to :func:`pyramid.url.resource_url` any +time the :term:`context` passed to ``resource_url`` is of class +``myapp.resources.MyRoot``. The asterisk following represents the type of interface that must be possessed by the :term:`request` (in this case, any interface, represented by asterisk). diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index da77a28f0..f9dab9ff6 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -171,7 +171,7 @@ match is straightforward. When a route is matched: Root factories related to a route were explained previously within :ref:`route_factories`. Both the global root factory and default root factory were explained previously within - :ref:`the_object_graph`. + :ref:`the_resource_tree`. .. _using_traverse_in_a_route_pattern: @@ -226,7 +226,7 @@ we've created a root factory that looks like so in a module named .. code-block:: python :linenos: - class Traversable(object): + class Resource(object): def __init__(self, subobjects): self.subobjects = subobjects @@ -234,15 +234,15 @@ we've created a root factory that looks like so in a module named return self.subobjects[name] root = Traversable( - {'a':Traversable({'b':Traversable({'c':Traversable({})})})} + {'a':Resource({'b':Resource({'c':Resource({})})})} ) def root_factory(request): return root -Above, we've defined a (bogus) graph that can be traversed, and a -``root_factory`` function that can be used as part of a particular -route configuration statement: +Above, we've defined a (bogus) resource tree that can be traversed, and a +``root_factory`` function that can be used as part of a particular route +configuration statement: .. code-block:: python :linenos: @@ -250,14 +250,13 @@ route configuration statement: config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') -The ``factory`` above points at the function we've defined. It will -return an instance of the ``Traversable`` class as a root object -whenever this route is matched. Instances of the``Traversable`` class -can be used for graph traversal because they have a ``__getitem__`` -method that does something nominally useful. Since traversal uses -``__getitem__`` to walk the nodes of an object graph, using traversal -against the root object implied by our route statement is a reasonable -thing to do. +The ``factory`` above points at the function we've defined. It will return +an instance of the ``Traversable`` class as a root object whenever this route +is matched. Instances of the``Resource`` class can be used for tree +traversal because they have a ``__getitem__`` method that does something +nominally useful. Since traversal uses ``__getitem__`` to walk the resources +of a resource tree, using traversal against the root resource implied by our +route statement is a reasonable thing to do. .. note:: @@ -271,12 +270,11 @@ thing to do. When the route configuration named ``home`` above is matched during a request, the matchdict generated will be based on its pattern: -``{foo}/{bar}/*traverse``. The "capture value" implied by the -``*traverse`` element in the pattern will be used to traverse the -graph in order to find a context, starting from the root object -returned from the root factory. In the above example, the -:term:`root` object found will be the instance named ``root`` in -``routes.py``. +``{foo}/{bar}/*traverse``. The "capture value" implied by the ``*traverse`` +element in the pattern will be used to traverse the resource tree in order to +find a context, starting from the root object returned from the root factory. +In the above example, the :term:`root` object found will be the instance +named ``root`` in ``routes.py``. If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``, is ``http://example.com/one/two/a/b/c``, the traversal path used @@ -284,12 +282,11 @@ against the root object will be ``a/b/c``. As a result, :app:`Pyramid` will attempt to traverse through the edges ``a``, ``b``, and ``c``, beginning at the root object. -In our above example, this particular set of traversal steps will mean -that the :term:`context` of the view would be the ``Traversable`` -object we've named ``c`` in our bogus graph and the :term:`view name` -resulting from traversal will be the empty string; if you need a -refresher about why this outcome is presumed, see -:ref:`traversal_algorithm`. +In our above example, this particular set of traversal steps will mean that +the :term:`context` of the view would be the ``Traversable`` object we've +named ``c`` in our bogus resource tree and the :term:`view name` resulting +from traversal will be the empty string; if you need a refresher about why +this outcome is presumed, see :ref:`traversal_algorithm`. At this point, a suitable view callable will be found and invoked using :term:`view lookup` as described in :ref:`view_configuration`, @@ -347,7 +344,7 @@ above ``mypackage.views.another_view`` view will be invoked when: - the :term:`context` is any object. For instance, if the URL ``http://example.com/one/two/a/another`` is provided -to an application that uses the previously mentioned object graph, the +to an application that uses the previously mentioned resource tree, the ``mypackage.views.another`` view callable will be called instead of the ``mypackage.views.myview`` view callable because the :term:`view name` will be ``another`` instead of the empty string. diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 7c725690d..0d8077fc4 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -144,10 +144,9 @@ it only supplies a mechanism to map URLs to :term:`view` code, along with a set of conventions for calling those views. You are free to use third-party components that fit your needs in your applications. -The concepts of :term:`view` and :term:`model` are used by -:app:`Pyramid` mostly as they would be by Django. -:app:`Pyramid` has a documentation culture more like Django's than -like Zope's. +The concept of :term:`view` is used by :app:`Pyramid` mostly as it would be +by Django. :app:`Pyramid` has a documentation culture more like Django's +than like Zope's. Like :term:`Pylons` version 1.0, but unlike :term:`Zope`, a :app:`Pyramid` application developer may use completely imperative @@ -177,13 +176,12 @@ frameworks, :app:`Pyramid` also generally fits into this class. .. sidebar:: You Say :app:`Pyramid` is MVC, But Where's The Controller? - The :app:`Pyramid` authors believe that the MVC pattern just - doesn't really fit the web very well. In a :app:`Pyramid` - application, there are models, which store data, and views, which - present the data stored in models. However, no facility provided - by the framework actually maps to the concept of a "controller". - So :app:`Pyramid` is actually an "MV" framework rather than an - "MVC" framework. "MVC", however, is close enough as a general - classification moniker for purposes of comparison with other web - frameworks. + The :app:`Pyramid` authors believe that the MVC pattern just doesn't + really fit the web very well. In a :app:`Pyramid` application, there is a + model, which stores data, and views, which present the data stored in the + model. However, no facility provided by the framework actually maps to + the concept of a "controller". So :app:`Pyramid` is actually an "MV" + framework rather than an "MVC" framework. "MVC", however, is close enough + as a general classification moniker for purposes of comparison with other + web frameworks. diff --git a/docs/narr/modelgraphtraverser.png b/docs/narr/modelgraphtraverser.png deleted file mode 100644 index 037d12b18..000000000 Binary files a/docs/narr/modelgraphtraverser.png and /dev/null differ diff --git a/docs/narr/models.rst b/docs/narr/models.rst deleted file mode 100644 index 91828287f..000000000 --- a/docs/narr/models.rst +++ /dev/null @@ -1,331 +0,0 @@ -Models -====== - -A :term:`model` class is typically a simple Python class defined in a module. -References to these classes and instances of such classes are omnipresent in -:app:`Pyramid` when :term:`traversal` is used to build an application: - -- Model instances make up the object graph that :app:`Pyramid` - will walk over when :term:`traversal` is used. - -- The ``context`` and ``containment`` arguments to - :meth:`pyramid.config.Configurator.add_view` often - reference a model class. - -- A :term:`root factory` returns a model instance. - -- A model instance is exposed to :term:`view` code as the :term:`context` of - a view. - -In many :term:`traversal` based applications (particularly ones that use -Zope-like patterns), model objects often store data and offer methods related -to mutating that data. - -.. note:: - - A terminology overlap confuses people who write applications that - always use ORM packages such as SQLAlchemy, which has a very - different notion of the definition of a "model". When using the API - of common ORM packages, its conception of "model" is almost - certainly not the same conception of "model" used by - :app:`Pyramid`. In particular, it can be unnatural to think of - :app:`Pyramid` model objects as "models" if you develop your - application using :term:`traversal` and a relational database. When - you develop such applications, the object graph *might* be composed - completely of "model" objects (as defined by the ORM) but it also - might not be. The things that :app:`Pyramid` refers to as - "models" in such an application may instead just be stand-ins that - perform a query and generate some wrapper *for* an ORM "model" or - set of ORM models. This naming overlap is slightly unfortunate. - However, many :app:`Pyramid` applications (especially ones which - use :term:`ZODB`) do indeed traverse a graph full of literal model - nodes. Each node in the graph is a separate persistent object that - is stored within a database. This was the use case considered when - coming up with the "model" terminology. However, if we had it to do - all over again, we'd probably call these objects something - different to avoid confusion. - -.. index:: - single: model constructor - -Defining a Model Constructor ----------------------------- - -An example of a model constructor, ``BlogEntry`` is presented below. -It is implemented as a class which, when instantiated, becomes a model -instance. - -.. code-block:: python - :linenos: - - import datetime - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - -A model constructor may be essentially any Python object which is -callable, and which returns a model instance. In the above example, -the ``BlogEntry`` class can be called, returning a model instance. - -.. index:: - single: model interfaces - -.. _models_which_implement_interfaces: - -Model Instances Which Implement Interfaces ------------------------------------------- - -Model instances can optionally be made to implement an -:term:`interface`. An interface is used to tag a model object with a -"type" that can later be referred to within :term:`view -configuration`. - -Specifying an interface instead of a class as the ``context`` or -``containment`` arguments within :term:`view configuration` statements -effectively makes it possible to use a single view callable for more -than one class of object. If your application is simple enough that -you see no reason to want to do this, you can skip reading this -section of the chapter. - -For example, here's some code which describes a blog entry which also -declares that the blog entry implements an :term:`interface`. - -.. code-block:: python - :linenos: - - import datetime - from zope.interface import implements - from zope.interface import Interface - - class IBlogEntry(Interface): - pass - - class BlogEntry(object): - implements(IBlogEntry) - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - -This model consists of two things: the class which defines the model -constructor (above as the class ``BlogEntry``), and an -:term:`interface` attached to the class (via an ``implements`` -statement at class scope using the ``IBlogEntry`` interface as its -sole argument). - -The interface object used must be an instance of a class that inherits -from :class:`zope.interface.Interface`. - -A model class may *implement* zero or more interfaces. You specify -that a model implements an interface by using the -:func:`zope.interface.implements` function at class scope. The above -``BlogEntry`` model implements the ``IBlogEntry`` interface. - -You can also specify that a *particular* model instance provides an -interface, as opposed to its class as above, which implies that all -instances do. To do so, use the :func:`zope.interface.directlyProvides` -function: - -.. code-block:: python - :linenos: - - from zope.interface import directlyProvides - from zope.interface import Interface - - class IBlogEntry(Interface): - pass - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - - entry = BlogEntry('title', 'body', 'author') - directlyProvides(entry, IBlogEntry) - -:func:`zope.interface.directlyProvides` will replace any existing -interface that was previously provided by an instance. If a model -object already has instance-level interface declarations that you -don't want to replace, use the :func:`zope.interface.alsoProvides` -function: - -.. code-block:: python - :linenos: - - from zope.interface import alsoProvides - from zope.interface import directlyProvides - from zope.interface import Interface - - class IBlogEntry1(Interface): - pass - - class IBlogEntry2(Interface): - pass - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - - entry = BlogEntry('title', 'body', 'author') - directlyProvides(entry, IBlogEntry1) - alsoProvides(entry, IBlogEntry2) - -:func:`zope.interface.alsoProvides` will augment the set of interfaces -directly provided by an instance instead of overwriting them like -:func:`zope.interface.directlyProvides` does. - -For more information about how model interfaces can be used by view -configuration, see :ref:`using_model_interfaces`. - -.. index:: - single: model graph - single: traversal graph - single: object graph - single: container nodes - single: leaf nodes - -Defining a Graph of Model Instances for Traversal -------------------------------------------------- - -When :term:`traversal` is used (as opposed to a purely :term:`url -dispatch` based application), :app:`Pyramid` expects to be able to -traverse a graph composed of model instances. Traversal begins at a -root model instance, and descends into the graph recursively via each -found model's ``__getitem__`` method. :app:`Pyramid` imposes the -following policy on model instance nodes in the graph: - -- Container nodes (i.e., nodes which contain other nodes) must supply - a ``__getitem__`` method which is willing to resolve a unicode name - to a subobject. If a subobject by that name does not exist in the - container, ``__getitem__`` must raise a :exc:`KeyError`. If a - subobject by that name *does* exist, the container should return the - subobject (another model instance). - -- Leaf nodes, which do not contain other nodes, must not - implement a ``__getitem__``, or if they do, their ``__getitem__`` - method must raise a :exc:`KeyError`. - -See :ref:`traversal_chapter` for more information about how traversal -works against model instances. - -.. index:: - pair: location-aware; model - -.. _location_aware: - -Location-Aware Model Instances ------------------------------- - -Applications which use :term:`traversal` to locate the :term:`context` -of a view must ensure that the model instances that make up the model -graph are "location aware". - -In order for :app:`Pyramid` location, security, URL-generation, and -traversal functions (i.e., functions in :ref:`location_module`, -:ref:`traversal_module`, :ref:`url_module` and some in -:ref:`security_module` ) to work properly against the model instances in -an object graph, all nodes in the graph must be :term:`location` -aware. -This means they must have two attributes: ``__parent__`` and -``__name__``. - -The ``__parent__`` attribute should be a reference to the node's -parent model instance in the graph. The ``__name__`` attribute should -be the name that a node's parent refers to the node via -``__getitem__``. - -The ``__parent__`` of the root object should be ``None`` and its -``__name__`` should be the empty string. For instance: - -.. code-block:: python - :linenos: - - class MyRootObject(object): - __name__ = '' - __parent__ = None - -A node returned from the root item's ``__getitem__`` method should have -a ``__parent__`` attribute that is a reference to the root object, and -its ``__name__`` attribute should match the name by which it is -reachable via the root object's ``__getitem__``. A container under the -root should have a ``__getitem__`` that returns objects with a -``__parent__`` attribute that points at the container, and these -subobjects should have a ``__name__`` attribute that matches the name by -which they are retrieved from the container via ``__getitem__``. -This pattern continues recursively down the graph. - -The ``__parent__`` attributes of each node form a linked list -that points "upward" toward the root. This is analogous to the -`..` entry in filesystem directories. If you follow the ``__parent__`` -values from any node in the traversal graph, you will eventually -come to the root node, just like if you keep executing the -``cd ..`` filesystem command, eventually you will reach the -filesystem root directory. - -.. warning:: If your root model object has a ``__name__`` argument - that is not ``None`` or the empty string, URLs returned by the - :func:`pyramid.url.model_url` function and paths generated by - the :func:`pyramid.traversal.model_path` and - :func:`pyramid.traversal.model_path_tuple` APIs will be - generated improperly. The value of ``__name__`` will be prepended - to every path and URL generated (as opposed to a single leading - slash or empty tuple element). - -.. sidebar:: Using :mod:`pyramid_traversalwrapper` - - If you'd rather not manage the ``__name__`` and ``__parent__`` - attributes of your models "by hand", an add-on package named - :mod:`pyramid_traversalwrapper` can help. - - In order to use this helper feature, you must first install the - :mod:`pyramid_traversalwrapper` package (available via PyPI), then register - its ``ModelGraphTraverser`` as the traversal policy, rather than the - default :app:`Pyramid` traverser. The package contains instructions for - doing so. - - Once :app:`Pyramid` is configured with this feature, you will no - longer need to manage the ``__parent__`` and ``__name__`` attributes - on graph objects "by hand". Instead, as necessary, during traversal - :app:`Pyramid` will wrap each object (even the root object) in a - ``LocationProxy`` which will dynamically assign a ``__name__`` and a - ``__parent__`` to the traversed object (based on the last traversed - object and the name supplied to ``__getitem__``). The root object - will have a ``__name__`` attribute of ``None`` and a ``__parent__`` - attribute of ``None``. - -.. index:: - single: model API functions - single: url generation (traversal) - -:app:`Pyramid` API Functions That Act Against Models -------------------------------------------------------- - -A model instance is used as the :term:`context` provided to a view. See -:ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information -about how a model instance becomes the context. - -The APIs provided by :ref:`traversal_module` are used against model -instances. These functions can be used to find the "path" of a model, -the root model in an object graph, or generate a URL to a model. - -The APIs provided by :ref:`location_module` are used against model -instances. These can be used to walk down an object graph, or -conveniently locate one object "inside" another. - -Some APIs in :ref:`security_module` accept a model object as a -parameter. For example, the -:func:`pyramid.security.has_permission` API accepts a "context" (a -model object) as one of its arguments; the ACL is obtained from this -model or one of its ancestors. Other APIs in the -:mod:`pyramid.security` module also accept :term:`context` as an -argument, and a context is always a model. - diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 2b03f7373..65ef3c526 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -258,8 +258,8 @@ The Interactive Shell Once you've installed your program for development using ``setup.py develop``, you can use an interactive Python shell to examine your -:app:`Pyramid` application :term:`model` and :term:`view` objects from -a Python prompt. To do so, use the ``paster`` shell command with the +:app:`Pyramid` application :term:`resource` and :term:`view` objects from a +Python prompt. To do so, use the ``paster`` shell command with the ``pshell`` argument: The first argument to ``pshell`` is the path to your application's ``.ini`` @@ -290,7 +290,7 @@ the name ``MyProject`` as a section name: Type "help" for more information. "root" is the Pyramid app root object, "registry" is the Pyramid registry object. >>> root - + >>> registry >>> registry.settings['debug_notfound'] @@ -868,7 +868,7 @@ named ``MyModel`` that provides the behavior. #. Line 6 is a "root factory" function that will be called by the :app:`Pyramid` *Router* for each request when it wants to find - the root of the object graph. Conventionally this is called + the root of the resource tree. Conventionally this is called ``get_root``. In a "real" application, the root object would not be such a simple diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index c87157472..05e108494 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -1,274 +1,319 @@ -.. index:: - single: resources - -.. _resources_chapter: - Resources ========= -A :term:`resource` is any file contained within a Python -:term:`package` which is *not* a Python source code file. For -example, each of the following is a resource: - -- a :term:`Chameleon` template file contained within a Python package. - -- a GIF image file contained within a Python package. - -- a CSS file contained within a Python package. - -- a JavaScript source file contained within a Python package. - -- A directory within a package that does not have an ``__init__.py`` - in it (if it possessed an ``__init__.py`` it would *be* a package). +A :term:`resource` is an object that represents a "place" in your +application. Every :app:`Pyramid` application has at least one resource +object: the :term:`root resource`. The root resource is the root of a +:term:`resource tree`. A resource tree is a set of nested dictionary-like +objects which you may use to represent your website's structure. + +In an application which uses :term:`traversal` to map URLs to code, the +resource tree structure is used heavily to map a URL to a :term:`view +callable`. :app:`Pyramid` will walk "up" the resource tree when +:term:`traversal` is used in order to find a :term:`context`. Once a context +is found, the resource represented by the context combined with data in the +request will be used to find a :term:`view callable`. + +In an application which uses :term:`URL dispatch`, the resource tree is only +used indirectly, and is often "invisible" to the developer. In URL dispatch +applications, the resource "tree" is often composed of only the root resource +by itself. This root resource sometimes has security declarations attached +to it, but is not required to have any. In general, the resource tree is +much less important in applications that use URL dispatch than applications +that use traversal. + +In "Zope-like" :app:`Pyramid` applications, resource objects also often store +data persistently and offer methods related to mutating that persistent data. +In these kinds of applications, resources not only represent the site +structure of your website, but they become the :term:`model` of the +application. + +Also: + +- The ``context`` and ``containment`` predicate arguments to + :meth:`pyramid.config.Configurator.add_view` (or a + :func:`pyramid.view.view_config` decorator) and reference a resource class + or resource :term:`interface`. + +- A :term:`root factory` returns a resource. + +- A resource is exposed to :term:`view` code as the :term:`context` of a + view. -The use of resources is quite common in most web development projects. -For example, when you create a :app:`Pyramid` application using one -of the available "paster" templates, as described in -:ref:`creating_a_project`, the directory representing the application -contains a Python :term:`package`. Within that Python package, there -are directories full of files which are resources. For example, there -is a ``templates`` directory which contains ``.pt`` files, and a -``static`` directory which contains ``.css``, ``.js``, and ``.gif`` -files. - -.. _understanding_resources: +.. index:: + single: resource constructor -Understanding Resources ------------------------ +Defining a Resource Constructor +------------------------------- -Let's imagine you've created a :app:`Pyramid` application that uses -a :term:`Chameleon` ZPT template via the -:func:`pyramid.chameleon_zpt.render_template_to_response` API. For -example, the application might address the resource named -``templates/some_template.pt`` using that API within a ``views.py`` -file inside a ``myapp`` package: +An example of a resource constructor, ``BlogEntry`` is presented below. It +is implemented as a class which, when instantiated, becomes a resource +instance. -.. ignore-next-block .. code-block:: python :linenos: - from pyramid.chameleon_zpt import render_template_to_response - render_template_to_response('templates/some_template.pt') - -"Under the hood", when this API is called, :app:`Pyramid` attempts -to make sense out of the string ``templates/some_template.pt`` -provided by the developer. To do so, it first finds the "current" -package. The "current" package is the Python package in which the -``views.py`` module which contains this code lives. This would be the -``myapp`` package, according to our example so far. By resolving the -current package, :app:`Pyramid` has enough information to locate -the actual template file. These are the elements it needs: - -- The *package name* (``myapp``) + import datetime -- The *resource name* (``templates/some_template.pt``) + class BlogEntry(object): + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() -:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the -package name and resource name to an absolute -(operating-system-specific) file name. It eventually passes this -resolved absolute filesystem path to the Chameleon templating engine, -which then uses it to load, parse, and execute the template file. - -Package names often contain dots. For example, ``pyramid`` is a -package. Resource names usually look a lot like relative UNIX file -paths. +A resource constructor may be any Python object which is callable, and which +returns a resource instance. In the above example, the ``BlogEntry`` class +can be "called", returning a resource instance. .. index:: - pair: overriding; resources + single: resource interfaces -.. _overriding_resources_section: +.. _resources_which_implement_interfaces: -Overriding Resources --------------------- +Resources Which Implement Interfaces +------------------------------------ -It can often be useful to override specific resources "from outside" a -given :app:`Pyramid` application. For example, you may wish to -reuse an existing :app:`Pyramid` application more or less -unchanged. However, some specific template file owned by the -application might have inappropriate HTML, or some static resource -(such as a logo file or some CSS file) might not be appropriate. You -*could* just fork the application entirely, but it's often more -convenient to just override the resources that are inappropriate and -reuse the application "as is". This is particularly true when you -reuse some "core" application over and over again for some set of -customers (such as a CMS application, or some bug tracking -application), and you want to make arbitrary visual modifications to a -particular application deployment without forking the underlying code. +Resources can optionally be made to implement an :term:`interface`. An +interface is used to tag a resource object with a "type" that can later be +referred to within :term:`view configuration`. -To this end, :app:`Pyramid` contains a feature that makes it -possible to "override" one resource with one or more other resources. -In support of this feature, a :term:`ZCML` directive exists named -``resource``. The ``resource`` directive allows you to *override* the -following kinds of resources defined in any Python package: +Specifying an interface instead of a class as the ``context`` or +``containment`` predicate arguments within :term:`view configuration` +statements effectively makes it possible to use a single view callable for +more than one class of resource object. If your application is simple enough +that you see no reason to want to do this, you can skip reading this section +of the chapter. -- Individual :term:`Chameleon` templates. +For example, here's some code which describes a blog entry which also +declares that the blog entry implements an :term:`interface`. -- A directory containing multiple Chameleon templates. +.. code-block:: python + :linenos: -- Individual static files served up by an instance of the - ``pyramid.view.static`` helper class. + import datetime + from zope.interface import implements + from zope.interface import Interface -- A directory of static files served up by an instance of the - ``pyramid.view.static`` helper class. + class IBlogEntry(Interface): + pass -- Any other resource (or set of resources) addressed by code that uses - the setuptools :term:`pkg_resources` API. + class BlogEntry(object): + implements(IBlogEntry) + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() -.. index:: - single: override_resource +This resource consists of two things: the class which defines the resource +constructor as the class ``BlogEntry``, and an :term:`interface` attached to +the class via an ``implements`` statement at class scope using the +``IBlogEntry`` interface as its sole argument. -.. _override_resource: +The interface object used must be an instance of a class that inherits from +:class:`zope.interface.Interface`. -The ``override_resource`` API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A resource class may implement zero or more interfaces. You specify that a +resource implements an interface by using the +:func:`zope.interface.implements` function at class scope. The above +``BlogEntry`` resource implements the ``IBlogEntry`` interface. -An individual call to -:meth:`pyramid.config.Configurator.override_resource` can -override a single resource. For example: +You can also specify that a particular resource *instance* provides an +interface, as opposed to its class. When you declare that a class implements +an interface, all instances of that class will also provide that interface. +However, you can also just say that a single object provides the interface. +To do so, use the :func:`zope.interface.directlyProvides` function: -.. ignore-next-block .. code-block:: python :linenos: - config.override_resource( - to_override='some.package:templates/mytemplate.pt', - override_with='another.package:othertemplates/anothertemplate.pt') + from zope.interface import directlyProvides + from zope.interface import Interface -The string value passed to both ``to_override`` and ``override_with`` -attached to a resource directive is called a "specification". The -colon separator in a specification separates the *package name* from -the *resource name*. The colon and the following resource name are -optional. If they are not specified, the override attempts to resolve -every lookup into a package from the directory of another package. -For example: + class IBlogEntry(Interface): + pass -.. ignore-next-block -.. code-block:: python - :linenos: + class BlogEntry(object): + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() - config.override_resource(to_override='some.package', - override_with='another.package') + entry = BlogEntry('title', 'body', 'author') + directlyProvides(entry, IBlogEntry) -Individual subdirectories within a package can also be overridden: +:func:`zope.interface.directlyProvides` will replace any existing interface +that was previously provided by an instance. If a resource object already +has instance-level interface declarations that you don't want to replace, use +the :func:`zope.interface.alsoProvides` function: -.. ignore-next-block .. code-block:: python :linenos: - config.override_resource(to_override='some.package:templates/', - override_with='another.package:othertemplates/') - + from zope.interface import alsoProvides + from zope.interface import directlyProvides + from zope.interface import Interface -If you wish to override a directory with another directory, you *must* -make sure to attach the slash to the end of both the ``to_override`` -specification and the ``override_with`` specification. If you fail to -attach a slash to the end of a specification that points to a directory, -you will get unexpected results. + class IBlogEntry1(Interface): + pass -You cannot override a directory specification with a file -specification, and vice versa: a startup error will occur if you try. -You cannot override a resource with itself: a startup error will occur -if you try. + class IBlogEntry2(Interface): + pass -Only individual *package* resources may be overridden. Overrides will -not traverse through subpackages within an overridden package. This -means that if you want to override resources for both -``some.package:templates``, and ``some.package.views:templates``, you -will need to register two overrides. - -The package name in a specification may start with a dot, meaning that -the package is relative to the package in which the configuration -construction file resides (or the ``package`` argument to the -:class:`pyramid.config.Configurator` class construction). -For example: - -.. ignore-next-block -.. code-block:: python - :linenos: + class BlogEntry(object): + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() - config.override_resource(to_override='.subpackage:templates/', - override_with='another.package:templates/') + entry = BlogEntry('title', 'body', 'author') + directlyProvides(entry, IBlogEntry1) + alsoProvides(entry, IBlogEntry2) -Multiple ``override_resource`` statements which name a shared -``to_override`` but a different ``override_with`` specification can be -"stacked" to form a search path. The first resource that exists in -the search path will be used; if no resource exists in the override -path, the original resource is used. +:func:`zope.interface.alsoProvides` will augment the set of interfaces +directly provided by an instance instead of overwriting them like +:func:`zope.interface.directlyProvides` does. -Resource overrides can actually override resources other than -templates and static files. Any software which uses the -:func:`pkg_resources.get_resource_filename`, -:func:`pkg_resources.get_resource_stream` or -:func:`pkg_resources.get_resource_string` APIs will obtain an -overridden file when an override is used. +For more information about how resource interfaces can be used by view +configuration, see :ref:`using_resource_interfaces`. .. index:: - pair: ZCML directive; resource + single: resource tree + single: traversal tree + single: object tree + single: container resources + single: leaf resources + +Defining a Resource Tree +------------------------ + +When :term:`traversal` is used (as opposed to a purely :term:`url dispatch` +based application), :app:`Pyramid` expects to be able to traverse a tree +composed of resources (the :term:`resource tree`). Traversal begins at a +root resource, and descends into the tree recursively via each resource's +``__getitem__`` method. :app:`Pyramid` imposes the following policy on +resource instances in the tree: + +- A container resource (a resource which contains other resources) must + supply a ``__getitem__`` method which is willing to resolve a unicode name + to a sub-resource. If a sub-resource by a particular name does not exist + in a container resource, ``__getitem__`` method of the container resource + must raise a :exc:`KeyError`. If a sub-resource by that name *does* exist, + the container's ``__getitem__`` should return the sub-resource. + +- Leaf resources, which do not contain other resources, must not implement a + ``__getitem__``, or if they do, their ``__getitem__`` method must raise a + :exc:`KeyError`. + +See :ref:`traversal_chapter` for more information about how traversal +works against resource instances. -.. _resource_zcml_directive: +.. index:: + pair: location-aware; resource -The ``resource`` ZCML Directive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _location_aware: -Instead of using -:meth:`pyramid.config.Configurator.override_resource` during -:term:`imperative configuration`, an equivalent can be used to perform -all the tasks described above within :term:`ZCML`. The ZCML -``resource`` tag is a frontend to using ``override_resource``. +Location-Aware Resources +------------------------ -An individual :app:`Pyramid` ``resource`` ZCML statement can -override a single resource. For example: +Applications which use :term:`traversal` to locate the :term:`context` +resource of a view must ensure that the resources that make up the +resource tree are "location aware". -.. code-block:: xml - :linenos: +In order for :app:`Pyramid` location, security, URL-generation, and traversal +functions (i.e., functions in :ref:`location_module`, +:ref:`traversal_module`, :ref:`url_module` and some in :ref:`security_module` +) to work properly against the resources in a resource tree, all resources in +the tree must be :term:`location` -aware. This means they must have two +attributes: ``__parent__`` and ``__name__``. - +The ``__parent__`` attribute should be a reference to the resource's parent +resource instance in the tree. The ``__name__`` attribute should be the name +with which a resource's parent refers to the resource via ``__getitem__``. -The string value passed to both ``to_override`` and ``override_with`` -attached to a resource directive is called a "specification". The -colon separator in a specification separates the *package name* from -the *resource name*. The colon and the following resource name are -optional. If they are not specified, the override attempts to resolve -every lookup into a package from the directory of another package. -For example: +The ``__parent__`` of the root resource should be ``None`` and its +``__name__`` should be the empty string. For instance: -.. code-block:: xml +.. code-block:: python :linenos: - - -Individual subdirectories within a package can also be overridden: + class MyRootResource(object): + __name__ = '' + __parent__ = None + +A resource returned from the root resource's ``__getitem__`` method should +have a ``__parent__`` attribute that is a reference to the root resource, and +its ``__name__`` attribute should match the name by which it is reachable via +the root resource's ``__getitem__``. A container resource within the root +resource should have a ``__getitem__`` that returns resources with a +``__parent__`` attribute that points at the container, and these subobjects +should have a ``__name__`` attribute that matches the name by which they are +retrieved from the container via ``__getitem__``. This pattern continues +recursively "up" the tree from the root. + +The ``__parent__`` attributes of each resource form a linked list that points +"upward" toward the root. This is analogous to the `..` entry in filesystem +directories. If you follow the ``__parent__`` values from any resource in the +resource tree, you will eventually come to the root resource, just like if +you keep executing the ``cd ..`` filesystem command, eventually you will +reach the filesystem root directory. + +.. warning:: If your root resource has a ``__name__`` argument + that is not ``None`` or the empty string, URLs returned by the + :func:`pyramid.url.resource_url` function and paths generated by + the :func:`pyramid.traversal.resource_path` and + :func:`pyramid.traversal.resource_path_tuple` APIs will be + generated improperly. The value of ``__name__`` will be prepended + to every path and URL generated (as opposed to a single leading + slash or empty tuple element). + +.. sidebar:: Using :mod:`pyramid_traversalwrapper` + + If you'd rather not manage the ``__name__`` and ``__parent__`` attributes + of your resources "by hand", an add-on package named + :mod:`pyramid_traversalwrapper` can help. + + In order to use this helper feature, you must first install the + :mod:`pyramid_traversalwrapper` package (available via PyPI), then register + its ``ModelGraphTraverser`` as the traversal policy, rather than the + default :app:`Pyramid` traverser. The package contains instructions for + doing so. + + Once :app:`Pyramid` is configured with this feature, you will no longer + need to manage the ``__parent__`` and ``__name__`` attributes on resource + objects "by hand". Instead, as necessary, during traversal :app:`Pyramid` + will wrap each resource (even the root resource) in a ``LocationProxy`` + which will dynamically assign a ``__name__`` and a ``__parent__`` to the + traversed resrouce (based on the last traversed resource and the name + supplied to ``__getitem__``). The root resource will have a ``__name__`` + attribute of ``None`` and a ``__parent__`` attribute of ``None``. -.. code-block:: xml - :linenos: +.. index:: + single: resource API functions + single: url generation (traversal) - +:app:`Pyramid` API Functions That Act Against Resources +------------------------------------------------------- -If you wish to override a directory with another directory, you *must* -make sure to attach the slash to the end of both the ``to_override`` -specification and the ``override_with`` specification. If you fail to -attach a slash to the end of a specification that points to a directory, -you will get unexpected results. +A resource object is used as the :term:`context` provided to a view. See +:ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information +about how a resource object becomes the context. -The package name in a specification may start with a dot, meaning that -the package is relative to the package in which the ZCML file resides. -For example: +The APIs provided by :ref:`traversal_module` are used against resource +objects. These functions can be used to find the "path" of a resource, the +root resource in a resource tree, or to generate a URL for a resource. -.. code-block:: xml - :linenos: +The APIs provided by :ref:`location_module` are used against resources. +These can be used to walk down a resource tree, or conveniently locate one +resource "inside" another. - +Some APIs in :ref:`security_module` accept a resource object as a parameter. +For example, the :func:`pyramid.security.has_permission` API accepts a +resource object as one of its arguments; the ACL is obtained from this +resource or one of its ancestors. Other APIs in the :mod:`pyramid.security` +module also accept :term:`context` as an argument, and a context is always a +resource. -See also :ref:`resource_directive`. diff --git a/docs/narr/resourcetreetraverser.png b/docs/narr/resourcetreetraverser.png new file mode 100644 index 000000000..037d12b18 Binary files /dev/null and b/docs/narr/resourcetreetraverser.png differ diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 5edbc3ec3..469d3d298 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -92,11 +92,11 @@ For example: Python name` values, each representing the dotted name path to a suitable implementation global defined at Python module scope. -The above configuration enables a policy which compares the value of -an "auth ticket" cookie passed in the request's environment which -contains a reference to a single :term:`principal` against the -principals present in any :term:`ACL` found in model data when -attempting to call some :term:`view`. +The above configuration enables a policy which compares the value of an "auth +ticket" cookie passed in the request's environment which contains a reference +to a single :term:`principal` against the principals present in any +:term:`ACL` found in the resource tree when attempting to call some +:term:`view`. While it is possible to mix and match different authentication and authorization policies, it is an error to pass an authentication @@ -136,7 +136,7 @@ permission using the :meth:`pyramid.config.Configurator.add_view` API: config.add_view('mypackage.views.blog_entry_add_view', name='add_entry.html', - context='mypackage.models.Blog', + context='mypackage.resources.Blog', permission='add') The equivalent view registration including the ``add`` permission name @@ -147,7 +147,7 @@ may be performed via the ``@view_config`` decorator: :linenos: from pyramid.view import view_config - from models import Blog + from resources import Blog @view_config(context=Blog, name='add_entry.html', permission='add') def blog_entry_add_view(request): @@ -208,19 +208,18 @@ When a default permission is registered: .. _assigning_acls: -Assigning ACLs to your Model Objects ------------------------------------- +Assigning ACLs to your Resource Objects +--------------------------------------- -When the default :app:`Pyramid` :term:`authorization policy` -determines whether a user possesses a particular permission in a -:term:`context`, it examines the :term:`ACL` associated with the -context. An ACL is associated with a context by virtue of the -``__acl__`` attribute of the model object representing the -:term:`context`. This attribute can be defined on the model -*instance* if you need instance-level security, or it can be defined -on the model *class* if you just need type-level security. +When the default :app:`Pyramid` :term:`authorization policy` determines +whether a user possesses a particular permission in a :term:`context`, it +examines the :term:`ACL` associated with the context. An ACL is associated +with a context by virtue of the ``__acl__`` attribute of the resource object +representing the :term:`context`. This attribute can be defined on the +resource *instance* if you need instance-level security, or it can be defined +on the resource *class* if you just need type-level security. -For example, an ACL might be attached to the model for a blog via its +For example, an ACL might be attached to the resource for a blog via its class: .. code-block:: python @@ -236,8 +235,8 @@ class: (Allow, 'group:editors', 'edit'), ] -Or, if your models are persistent, an ACL might be specified via the -``__acl__`` attribute of an *instance* of a model: +Or, if your resources are persistent, an ACL might be specified via the +``__acl__`` attribute of an *instance* of a resource: .. code-block:: python :linenos: @@ -256,11 +255,11 @@ Or, if your models are persistent, an ACL might be specified via the (Allow, 'group:editors', 'edit'), ] -Whether an ACL is attached to a model's class or an instance of the -model itself, the effect is the same. It is useful to decorate -individual model instances with an ACL (as opposed to just decorating -their class) in applications such as "CMS" systems where fine-grained -access is required on an object-by-object basis. +Whether an ACL is attached to a resource's class or an instance of the +resource itself, the effect is the same. It is useful to decorate individual +resource instances with an ACL (as opposed to just decorating their class) in +applications such as "CMS" systems where fine-grained access is required on +an object-by-object basis. .. index:: single: ACE @@ -448,16 +447,16 @@ the following: ACL Inheritance and Location-Awareness -------------------------------------- -While the default :term:`authorization policy` is in place, if a model -object does not have an ACL when it is the context, its *parent* is -consulted for an ACL. If that object does not have an ACL, *its* -parent is consulted for an ACL, ad infinitum, until we've reached the -root and there are no more parents left. +While the default :term:`authorization policy` is in place, if a resource +object does not have an ACL when it is the context, its *parent* is consulted +for an ACL. If that object does not have an ACL, *its* parent is consulted +for an ACL, ad infinitum, until we've reached the root and there are no more +parents left. -In order to allow the security machinery to perform ACL inheritance, -model objects must provide *location-awareness*. Providing -*location-awareness* means two things: the root object in the graph -must have a ``_name__`` attribute and a ``__parent__`` attribute. +In order to allow the security machinery to perform ACL inheritance, resource +objects must provide *location-awareness*. Providing *location-awareness* +means two things: the root object in the resource tree must have a +``_name__`` attribute and a ``__parent__`` attribute. .. code-block:: python :linenos: diff --git a/docs/narr/static.rst b/docs/narr/static.rst index 172fb08d4..6b03b2349 100644 --- a/docs/narr/static.rst +++ b/docs/narr/static.rst @@ -1,26 +1,26 @@ -Static Resources -================ +Static Assets +============= -:app:`Pyramid` makes it possible to serve up static -resources files from a directory on a filesystem. This chapter describes -how to configure :app:`Pyramid` to do so. +:app:`Pyramid` makes it possible to serve up static asset files from a +directory on a filesystem. This chapter describes how to configure +:app:`Pyramid` to do so. .. index:: single: add_static_view -.. _static_resources_section: +.. _static_assets_section: -Serving Static Resources ------------------------- +Serving Static Assets +--------------------- -Use the :meth:`pyramid.config.Configurator.add_static_view` to -instruct :app:`Pyramid` to serve static resources such as JavaScript and CSS -files. This mechanism makes static files available at a name relative to the -application root URL, e.g. ``/static``. +Use the :meth:`pyramid.config.Configurator.add_static_view` to instruct +:app:`Pyramid` to serve static assets such as JavaScript and CSS files. This +mechanism makes static files available at a name relative to the application +root URL, e.g. ``/static``. Note that the ``path`` provided to -:meth:`pyramid.config.Configurator.add_static_view` may be a fully -qualified :term:`resource specification`, or an *absolute path*. +:meth:`pyramid.config.Configurator.add_static_view` may be a fully qualified +:term:`asset specification`, or an *absolute path*. Here's an example of a use of :meth:`pyramid.config.Configurator.add_static_view` that will serve @@ -34,11 +34,10 @@ path. # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='/var/www/static') -Here's an example of -:meth:`pyramid.config.Configurator.add_static_view` that will serve -files up under the ``/static`` URL from the ``a/b/c/static`` directory of the -Python package named ``some_package`` using a fully qualified :term:`resource -specification`. +Here's an example of :meth:`pyramid.config.Configurator.add_static_view` that +will serve files up under the ``/static`` URL from the ``a/b/c/static`` +directory of the Python package named ``some_package`` using a fully +qualified :term:`asset specification`. .. code-block:: python :linenos: @@ -46,7 +45,7 @@ specification`. # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='some_package:a/b/c/static') -Whether you use for ``path`` a fully qualified resource specification, or an +Whether you use for ``path`` a fully qualified asset specification, or an absolute path, when you place your static files on the filesystem in the directory represented as the ``path`` of the directive, you will then be able to view the static files in this directory via a browser at URLs prefixed @@ -58,13 +57,12 @@ subdirectories recursively, and any subdirectories may hold files; these will be resolved by the static view as you would expect. While the ``path`` argument can be a number of different things, the ``name`` -argument of the call to -:meth:`pyramid.config.Configurator.add_static_view` can also be one of -a number of things: a *view name* or a *URL*. The above examples have shown -usage of the ``name`` argument as a view name. When ``name`` is a *URL* (or -any string with a slash (``/``) in it), static resources can be served from -an external webserver. In this mode, the ``name`` is used as the URL prefix -when generating a URL using :func:`pyramid.url.static_url`. +argument of the call to :meth:`pyramid.config.Configurator.add_static_view` +can also be one of a number of things: a *view name* or a *URL*. The above +examples have shown usage of the ``name`` argument as a view name. When +``name`` is a *URL* (or any string with a slash (``/``) in it), static assets +can be served from an external webserver. In this mode, the ``name`` is used +as the URL prefix when generating a URL using :func:`pyramid.url.static_url`. .. note:: @@ -130,18 +128,18 @@ imperative configuration for the same purpose. other than ``media_location`` could be used. .. index:: - single: generating static resource urls - single: static resource urls + single: generating static asset urls + single: static asset urls -.. _generating_static_resource_urls: +.. _generating_static_asset_urls: -Generating Static Resource URLs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Generating Static Asset URLs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :meth:`pyramid.config.Configurator.add_static_view` method is -used to register a static resource directory, a special helper API named -:func:`pyramid.url.static_url` can be used to generate the appropriate URL for a -package resource that lives in one of the directories named by the static +When a :meth:`pyramid.config.Configurator.add_static_view` method is used to +register a static asset directory, a special helper API named +:func:`pyramid.url.static_url` can be used to generate the appropriate URL +for an asset that lives in one of the directories named by the static registration ``path`` attribute. For example, let's assume you create a set of static declarations like so: @@ -149,19 +147,19 @@ For example, let's assume you create a set of static declarations like so: .. code-block:: python :linenos: - config.add_static_view(name='static1', path='mypackage:resources/1') - config.add_static_view(name='static2', path='mypackage:resources/2') + config.add_static_view(name='static1', path='mypackage:assets/1') + config.add_static_view(name='static2', path='mypackage:assets/2') These declarations create URL-accessible directories which have URLs that -begin with ``/static1`` and ``/static2``, respectively. The resources in -the ``resources/1`` directory of the ``mypackage`` package are consulted when -a user visits a URL which begins with ``/static1``, and the resources in the -``resources/2`` directory of the ``mypackage`` package are consulted when a -user visits a URL which begins with ``/static2``. +begin with ``/static1`` and ``/static2``, respectively. The assets in the +``assets/1`` directory of the ``mypackage`` package are consulted when a user +visits a URL which begins with ``/static1``, and the assets in the +``assets/2`` directory of the ``mypackage`` package are consulted when a user +visits a URL which begins with ``/static2``. -You needn't generate the URLs to static resources "by hand" in such a -configuration. Instead, use the :func:`pyramid.url.static_url` API -to generate them for you. For example: +You needn't generate the URLs to static assets "by hand" in such a +configuration. Instead, use the :func:`pyramid.url.static_url` API to +generate them for you. For example: .. code-block:: python :linenos: @@ -170,8 +168,8 @@ to generate them for you. For example: from pyramid.chameleon_zpt import render_template_to_response def my_view(request): - css_url = static_url('mypackage:resources/1/foo.css', request) - js_url = static_url('mypackage:resources/2/foo.js', request) + css_url = static_url('mypackage:assets/1/foo.css', request) + js_url = static_url('mypackage:assets/2/foo.js', request) return render_template_to_response('templates/my_template.pt', css_url = css_url, js_url = js_url) @@ -186,13 +184,12 @@ constructing static URLs "by hand" is that if you need to change the ``name`` of a static URL declaration, the generated URLs will continue to resolve properly after the rename. -URLs may also be generated by :func:`pyramid.url.static_url` to static -resources that live *outside* the :app:`Pyramid` application. This will -happen when the :meth:`pyramid.config.Configurator.add_static_view` -API associated with the path fed to :func:`pyramid.url.static_url` is a *URL* -instead of a view name. For example, the ``name`` argument may be -``http://example.com`` while the the ``path`` given may be -``mypackage:images``: +URLs may also be generated by :func:`pyramid.url.static_url` to static assets +that live *outside* the :app:`Pyramid` application. This will happen when +the :meth:`pyramid.config.Configurator.add_static_view` API associated with +the path fed to :func:`pyramid.url.static_url` is a *URL* instead of a view +name. For example, the ``name`` argument may be ``http://example.com`` while +the the ``path`` given may be ``mypackage:images``: .. code-block:: python :linenos: @@ -200,7 +197,7 @@ instead of a view name. For example, the ``name`` argument may be config.add_static_view(name='http://example.com/images', path='mypackage:images') Under such a configuration, the URL generated by ``static_url`` for -resources which begin with ``mypackage:images`` will be prefixed with +assets which begin with ``mypackage:images`` will be prefixed with ``http://example.com/images``: .. code-block:: python @@ -210,25 +207,23 @@ resources which begin with ``mypackage:images`` will be prefixed with # -> http://example.com/images/logo.png .. index:: - single: static resource view - -Advanced: Serving Static Resources Using a View Callable --------------------------------------------------------- - -For more flexibility, static resources can be served by a :term:`view -callable` which you register manually. For example, you may want -static resources to only be available when the :term:`context` of the -view is of a particular type, or when the request is of a particular -type. - -The :class:`pyramid.view.static` helper class is used to perform -this task. This class creates an object that is capable acting as a -:app:`Pyramid` view callable which serves static resources from a -directory. For instance, to serve files within a directory located on -your filesystem at ``/path/to/static/dir`` from the URL path -``/static`` in your application, create an instance of the -:class:`pyramid.view.static` class inside a ``static.py`` file in -your application root as below. + single: static assets view + +Advanced: Serving Static Assets Using a View Callable +----------------------------------------------------- + +For more flexibility, static assets can be served by a :term:`view callable` +which you register manually. For example, you may want static assets to only +be available when the :term:`context` is of a particular type, or when +certain request headers are present. + +The :class:`pyramid.view.static` helper class is used to perform this +task. This class creates an object that is capable acting as a :app:`Pyramid` +view callable which serves static assets from a directory. For instance, to +serve files within a directory located on your filesystem at +``/path/to/static/dir`` from the URL path ``/static`` in your application, +create an instance of the :class:`pyramid.view.static` class inside a +``static.py`` file in your application root as below. .. ignore-next-block .. code-block:: python @@ -240,47 +235,43 @@ your application root as below. .. note:: the argument to :class:`pyramid.view.static` can also be a "here-relative" pathname, e.g. ``my/static`` (meaning relative to the Python package of the module in which the view is being defined). - It can also be a :term:`resource specification` + It can also be a :term:`asset specification` (e.g. ``anotherpackage:some/subdirectory``). Subsequently, you may wire this view up to be accessible as ``/static`` using -the :mod:`pyramid.config.Configurator.add_view` method in your -application's startup code against either the class or interface that -represents your root object. +the :mod:`pyramid.config.Configurator.add_view` method in your application's +startup code against either the class or interface that represents your root +resource object. .. code-block:: python :linenos: config.add_view('mypackage.static.static_view', name='static', - context='mypackage.models.Root') + context='mypackage.resources.Root') -In this case, ``mypackage.models.Root`` refers to the class of your -:app:`Pyramid` application's traversal root object. +In this case, ``mypackage.resources.Root`` refers to the class of your +:app:`Pyramid` application's resource tree. -The context argument above limits where the static view is accessible to -URL paths directly under the root object. If you omit the ``context`` -argument, then ``static`` will be accessible as the static view against -any model object in the traversal graph. This will allow -``/static/foo.js`` to work, but it will also allow for -``/anything/static/foo.js`` too, as long as ``anything`` can be -resolved. +The context argument above limits where the static view is accessible to URL +paths directly under the root object. If you omit the ``context`` argument, +then ``static`` will be accessible as the static view against any resource +object in the resource tree. This will allow ``/static/foo.js`` to work, but +it will also allow for ``/anything/static/foo.js`` too, as long as +``anything`` can be resolved. Note that you cannot use the :func:`pyramid.url.static_url` API to generate -URLs against resources made accessible by registering a custom static view. +URLs against assets made accessible by registering a custom static view. .. warning:: - When adding a static view to your root object, you need to be - careful that there are no model objects contained in the - root with the same key as the view name (e.g., ``static``). - Model objects take precedence during traversal, - thus such a name collision will cause the model to "shadow" - your static view. To avoid this issue, and ensure that your - root object's ``__getitem__`` is never - called when a static resource is requested, you can refer to them - unambiguously using the ``@@`` prefix (goggles) in their URLs. - For the above examples you could use '/@@static/foo.js' - instead of '/static/foo.js' to avoid such shadowing. - See :ref:`traversal_chapter` for information - about "goggles" (``@@``). + When adding a static view to your root object, you need to be careful that + there are no resource objects contained in the root with the same key as + the view name (e.g., ``static``). Resource objects take precedence during + traversal, thus such a name collision will cause the resource to "shadow" + your static view. To avoid this issue, and ensure that your root + resource's ``__getitem__`` is never called when a static asset is + requested, you can refer to them unambiguously using the ``@@`` prefix + (goggles) in their URLs. For the above examples you could use + '/@@static/foo.js' instead of '/static/foo.js' to avoid such shadowing. + See :ref:`traversal_chapter` for information about "goggles" (``@@``). diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index 09b9bfc51..d6c21831e 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -468,20 +468,19 @@ works in these templates. Using ZPT Macros in :app:`Pyramid` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :term:`renderer` is used to render a template, :app:`Pyramid` -makes at least two top-level names available to the template by default: -``context`` and ``request``. One of the common needs in ZPT-based -templates is to use one template's "macros" from within a different -template. In Zope, this is typically handled by retrieving the template -from the ``context``. But the context in :app:`Pyramid` is typically a -model object, and templates cannot usually be retrieved from models. To -use macros in :app:`Pyramid`, you need to make the macro template itself -available to the rendered template by passing the macro template, or -even the macro itself, *into* the rendered template. To do this you can -use the :func:`pyramid.renderers.get_renderer` API to retrieve the macro -template, and pass it into the template being rendered via the dictionary -returned by the view. For example, using a :term:`view configuration` via a -:class:`pyramid.view.view_config` decorator that uses a +When a :term:`renderer` is used to render a template, :app:`Pyramid` makes at +least two top-level names available to the template by default: ``context`` +and ``request``. One of the common needs in ZPT-based templates is to use +one template's "macros" from within a different template. In Zope, this is +typically handled by retrieving the template from the ``context``. But the +context in :app:`Pyramid` is a resource object, and templates cannot usually +be retrieved from resources. To use macros in :app:`Pyramid`, you need to make +the macro template itself available to the rendered template by passing the +macro template, or even the macro itself, *into* the rendered template. To +do this you can use the :func:`pyramid.renderers.get_renderer` API to +retrieve the macro template, and pass it into the template being rendered via +the dictionary returned by the view. For example, using a :term:`view +configuration` via a :class:`pyramid.view.view_config` decorator that uses a :term:`renderer`: .. code-block:: python @@ -658,7 +657,7 @@ on, an exception resulting from the same problem might end like so: request: project: proj macros: - context: + context: view: NameError: wrong diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index f540fa255..171eaa1c7 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -110,11 +110,9 @@ follows: View callables already have access to the request (it's passed in to each as ``request``). -- ``get_current_request`` should never be called in :term:`model` - code. Model code should never require any access to the request; if - your model code requires access to a request object, you've almost - certainly factored something wrong, and you should change your code - rather than using this function. +- ``get_current_request`` should never be called in :term:`resource` code. + If a resource needs access to the request, it should be passed the request + by a :term:`view callable`. - ``get_current_request`` function should never be called because it's "easier" or "more elegant" to think about calling it than to pass a diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 01729c4bd..c8878224e 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -3,14 +3,14 @@ Traversal ========= -:term:`Traversal` is a :term:`context finding` mechanism. It is the -act of finding a :term:`context` and a :term:`view name` by walking -over an *object graph*, starting from a :term:`root` object, using a +:term:`Traversal` is a :term:`context finding` mechanism. It is the act of +finding a :term:`context` and a :term:`view name` by walking over a +:term:`resource tree`, starting from a :term:`root` resource, using a :term:`request` object as a source of path information. -In this chapter, we'll provide a high-level overview of traversal, -we'll explain the concept of an *object graph*, and we'll show how -traversal might be used within an application. +In this chapter, we'll provide a high-level overview of traversal, we'll +explain the concept of a resource tree, and we'll show how traversal might be +used within an application. .. index:: single: traversal analogy @@ -55,16 +55,16 @@ can run the ``cat`` command: The contents of ``myfile`` are now printed on the user's behalf. -:app:`Pyramid` is very much like this inexperienced UNIX user as it -uses :term:`traversal` against an object graph. In this analogy, we -can map the ``cat`` program to the :app:`Pyramid` concept of a -:term:`view callable`: it is a program that can be run against some -:term:`context` as the result of :term:`view lookup`. The file being -operated on in this analogy is the :term:`context` object; the context -is the "last node found" in a traversal. The directory structure is -the object graph being traversed. The act of progressively changing -directories to find the file as well as the handling of a ``cd`` error -as a stop condition is analogous to :term:`traversal`. +:app:`Pyramid` is very much like this inexperienced UNIX user as it uses +:term:`traversal` against a resource tree. In this analogy, we can map the +``cat`` program to the :app:`Pyramid` concept of a :term:`view callable`: it +is a program that can be run against some :term:`context` as the result of +:term:`view lookup`. The file being operated on in this analogy is the +:term:`context` object; the context is the "last resource found" in a +traversal. The directory structure is the resource tree being traversed. +The act of progressively changing directories to find the file as well as the +handling of a ``cd`` error as a stop condition is analogous to +:term:`traversal`. The analogy we've used is not *exactly* correct, because, while the naive user already knows which command he wants to invoke before he @@ -94,9 +94,9 @@ Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of path segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is converted to the sequence ``['a', 'b', 'c']``. -After the path info is converted, a lookup is performed against the -object graph for each path segment. Each lookup uses the -``__getitem__`` method of an object in the graph. +After the path info is converted, a lookup is performed against the resource +tree for each path segment. Each lookup uses the ``__getitem__`` method of +an object in the tree. For example, if the path info sequence is ``['a', 'b', 'c']``: @@ -121,13 +121,13 @@ This process continues until the path segment sequence is exhausted or a lookup for a path element fails. In either case, a :term:`context` is found. -Traversal "stops" when it either reaches a leaf level model instance -in your object graph or when the path segments implied by the URL "run -out". The object that traversal "stops on" becomes the -:term:`context`. If at any point during traversal any node in the -graph doesn't have a ``__getitem__`` method, or if the ``__getitem__`` -method of a node raises a :exc:`KeyError`, traversal ends immediately, -and that node becomes the :term:`context`. +Traversal "stops" when it either reaches a leaf level resource in your +resource tree or when the path segments implied by the URL "run out". The +object that traversal "stops on" becomes the :term:`context`. If at any +point during traversal any resource in the tree doesn't have a +``__getitem__`` method, or if the ``__getitem__`` method of a resource raises +a :exc:`KeyError`, traversal ends immediately, and that resource becomes the +:term:`context`. The results of a :term:`traversal` also include a :term:`view name`. The :term:`view name` is the *first* URL path segment in the set of @@ -142,18 +142,18 @@ request. How :app:`Pyramid` performs view lookup is explained within the :ref:`views_chapter` chapter. .. index:: - single: object graph - single: traversal graph - single: model graph + single: object tree + single: traversal tree + single: resource tree -.. _the_object_graph: +.. _the_resource_tree: -The Object Graph ----------------- +The Resource Tree +----------------- -When your application uses :term:`traversal` to resolve URLs to code, -your application must supply an *object graph* to :app:`Pyramid`. -This graph is represented by a :term:`root` object. +When your application uses :term:`traversal` to resolve URLs to code, your +application must supply the a resource tree to :app:`Pyramid`. This tree is +represented by a :term:`root` object. In order to supply a root object for an application, at system startup time, the :app:`Pyramid` :term:`Router` is configured with a @@ -188,19 +188,18 @@ alternately be passed to the ``Configurator`` as a :term:`dotted Python name` which refers to a root factory object defined in a different module. -A root factory is passed a :term:`request` object and it is expected -to return an object which represents the root of the object graph. -All :term:`traversal` will begin at this root object. Usually a root -factory for a traversal-based application will be more complicated -than the above ``Root`` object; in particular it may be associated -with a database connection or another persistence mechanism. A root -object is often an instance of a class which has a ``__getitem__`` -method. +A root factory is passed a :term:`request` object and it is expected to +return an object which represents the root of the resource tree. All +:term:`traversal` will begin at this root object. Usually a root factory for +a traversal-based application will be more complicated than the above +``Root`` object; in particular it may be associated with a database +connection or another persistence mechanism. A root object is often an +instance of a class which has a ``__getitem__`` method. If no :term:`root factory` is passed to the :app:`Pyramid` :term:`Configurator` constructor, or the ``root_factory`` is specified as the value ``None``, a *default* root factory is used. The default -root factory always returns an object that has no child nodes. +root factory always returns an object that has no child resources. .. sidebar:: Emulating the Default Root Factory @@ -218,47 +217,45 @@ root factory always returns an object that has no child nodes. config = Configurator(root_factory=Root) The default root factory is just a really stupid object that has no - behavior or state. Using :term:`traversal` against an application - that uses the object graph supplied by the default root object is - not very interesting, because the default root object has no - children. Its availability is more useful when you're developing - an application using :term:`URL dispatch`. - -Items contained within the object graph are sometimes analogous to the -concept of :term:`model` objects used by many other frameworks (and -:app:`Pyramid` APIs often refers to them as "models", as well). -They are typically instances of Python classes. - -The object graph consists of *container* nodes and *leaf* nodes. -There is only one difference between a *container* node and a *leaf* -node: *container* nodes possess a ``__getitem__`` method while *leaf* -nodes do not. The ``__getitem__`` method was chosen as the signifying -difference between the two types of nodes because the presence of this + behavior or state. Using :term:`traversal` against an application that + uses the resource tree supplied by the default root object is not very + interesting, because the default root object has no children. Its + availability is more useful when you're developing an application using + :term:`URL dispatch`. + +Items contained within the resource tree are sometimes, in certain types of +applications, analogous to the concept of :term:`model` objects used by many +other frameworks. They are typically instances of Python classes. + +The resource tree consists of *container* resources and *leaf* resources. +There is only one difference between a *container* resource and a *leaf* +resource: *container* resources possess a ``__getitem__`` method while *leaf* +resources do not. The ``__getitem__`` method was chosen as the signifying +difference between the two types of resources because the presence of this method is how Python itself typically determines whether an object is "containerish" or not. -Each container node is presumed to be willing to return a child node +Each container resource is presumed to be willing to return a child resource or raise a ``KeyError`` based on a name passed to its ``__getitem__``. Leaf-level instances must not have a ``__getitem__``. If instances that you'd like to be leaves already happen to have a ``__getitem__`` through some historical inequity, you should subclass -these node types and cause their ``__getitem__`` methods to simply +these resource types and cause their ``__getitem__`` methods to simply raise a ``KeyError``. Or just disuse them and think up another strategy. -Usually, the traversal root is a *container* node, and as such it -contains other nodes. However, it doesn't *need* to be a container. -Your object graph can be as shallow or as deep as you require. +Usually, the traversal root is a *container* resource, and as such it +contains other resources. However, it doesn't *need* to be a container. +Your resource tree can be as shallow or as deep as you require. -In general, the object graph is traversed beginning at its root object -using a sequence of path elements described by the ``PATH_INFO`` of -the current request; if there are path segments, the root object's -``__getitem__`` is called with the next path segment, and it is -expected to return another graph object. The resulting object's -``__getitem__`` is called with the very next path segment, and it is -expected to return another graph object. This happens *ad infinitum* -until all path segments are exhausted. +In general, the resource tree is traversed beginning at its root object using +a sequence of path elements described by the ``PATH_INFO`` of the current +request; if there are path segments, the root object's ``__getitem__`` is +called with the next path segment, and it is expected to return another +resource object. The resulting object's ``__getitem__`` is called with the +very next path segment, and it is expected to return another resource object. +This happens *ad infinitum* until all path segments are exhausted. .. index:: single: traversal algorithm @@ -273,7 +270,7 @@ This section will attempt to explain the :app:`Pyramid` traversal algorithm. We'll provide a description of the algorithm, a diagram of how the algorithm works, and some example traversal scenarios that might help you understand how the algorithm operates against a -specific object graph. +specific resource tree. We'll also talk a bit about :term:`view lookup`. The :ref:`views_chapter` chapter discusses :term:`view lookup` in detail, @@ -314,17 +311,15 @@ and a :term:`view name`. stripped off ``PATH_INFO``, and the remaining path segments are split on the slash character to form a traversal sequence. - The traversal algorithm by default attempts to first URL-unquote - and then Unicode-decode each path segment derived from - ``PATH_INFO`` from its natural byte string (``str`` type) - representation. URL unquoting is performed using the Python - standard library ``urllib.unquote`` function. Conversion from a - URL-decoded string into Unicode is attempted using the UTF-8 - encoding. If any URL-unquoted path segment in ``PATH_INFO`` is - not decodeable using the UTF-8 decoding, a :exc:`TypeError` is - raised. A segment will be fully URL-unquoted and UTF8-decoded - before it is passed it to the ``__getitem__`` of any model object - during traversal. + The traversal algorithm by default attempts to first URL-unquote and then + Unicode-decode each path segment derived from ``PATH_INFO`` from its + natural byte string (``str`` type) representation. URL unquoting is + performed using the Python standard library ``urllib.unquote`` function. + Conversion from a URL-decoded string into Unicode is attempted using the + UTF-8 encoding. If any URL-unquoted path segment in ``PATH_INFO`` is not + decodeable using the UTF-8 decoding, a :exc:`TypeError` is raised. A + segment will be fully URL-unquoted and UTF8-decoded before it is passed + it to the ``__getitem__`` of any resource during traversal. Thus, a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the traversal sequence ``[u'a', u'b', u'c']``. @@ -340,12 +335,11 @@ and a :term:`view name`. for the name ``c``, and may return "object ``c``". #. Traversal ends when a) the entire path is exhausted or b) when any - graph element raises a :exc:`KeyError` from its ``__getitem__`` or - c) when any non-final path element traversal does not have a - ``__getitem__`` method (resulting in a :exc:`NameError`) or d) - when any path element is prefixed with the set of characters - ``@@`` (indicating that the characters following the ``@@`` token - should be treated as a :term:`view name`). + resouce raises a :exc:`KeyError` from its ``__getitem__`` or c) when any + non-final path element traversal does not have a ``__getitem__`` method + (resulting in a :exc:`NameError`) or d) when any path element is prefixed + with the set of characters ``@@`` (indicating that the characters + following the ``@@`` token should be treated as a :term:`view name`). #. When traversal ends for any of the reasons in the previous step, the last object found during traversal is deemed to be the @@ -375,19 +369,18 @@ The traversal algorithm exposes two special cases: The default view is a view that is registered with no name or a view which is registered with a name that equals the empty string. -- If any path segment element begins with the special characters - ``@@`` (think of them as goggles), the value of that segment minus - the goggle characters is considered the :term:`view name` - immediately and traversal stops there. This allows you to address - views that may have the same names as model instance names in the - graph unambiguously. +- If any path segment element begins with the special characters ``@@`` + (think of them as goggles), the value of that segment minus the goggle + characters is considered the :term:`view name` immediately and traversal + stops there. This allows you to address views that may have the same names + as resource names in the tree unambiguously. Finally, traversal is responsible for locating a :term:`virtual root`. A virtual root is used during "virtual hosting"; see the :ref:`vhosting_chapter` chapter for information. We won't speak more about it in this chapter. -.. image:: modelgraphtraverser.png +.. image:: resourcetreetraverser.png .. index:: single: traversal examples @@ -397,13 +390,13 @@ Traversal Algorithm Examples No one can be expected to understand the traversal algorithm by analogy and description alone, so let's examine some traversal -scenarios that use concrete URLs and object graph compositions. +scenarios that use concrete URLs and resource tree compositions. Let's pretend the user asks for ``http://example.com/foo/bar/baz/biz/buz.txt``. The request's ``PATH_INFO`` in that case is ``/foo/bar/baz/biz/buz.txt``. Let's further pretend that when this request comes in that we're traversing -the following object graph: +the following resource tree: .. code-block:: text @@ -450,7 +443,7 @@ Let's say that view lookup finds no matching view type. In this circumstance, the :app:`Pyramid` :term:`router` returns the result of the :term:`not found view` and the request ends. -However, for this graph: +However, for this tree: .. code-block:: text @@ -537,6 +530,6 @@ The :mod:`pyramid.traversal` module contains API functions that deal with traversal, such as traversal invocation from within application code. -The :func:`pyramid.url.model_url` function generates a URL when -given an object retrieved from an object graph. +The :func:`pyramid.url.resource_url` function generates a URL when +given an object retrieved from an resource tree. diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index 3c31f69b6..6a721f403 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -12,23 +12,22 @@ your application. In this context, a "unit" is often a function or a method of a class instance. The unit is also referred to as a "unit under test". -The goal of a single unit test is to test **only** some permutation of -the "unit under test". If you write a unit test that aims to verify -the result of a particular codepath through a Python function, you -need only be concerned about testing the code that *lives in the -function body itself*. If the function accepts a parameter that -represents a complex application "domain object" (such as a model, a -database connection, or an SMTP server), the argument provided to this -function during a unit test *need not be* and likely *should not be* a -"real" implementation object. For example, although a particular -function implementation may accept an argument that represents an SMTP -server object, and the function may call a method of this object when -the system is operating normally that would result in an email being -sent, a unit test of this codepath of the function does *not* need to -test that an email is actually sent. It just needs to make sure that -the function calls the method of the object provided as an argument -that *would* send an email if the argument happened to be the "real" -implementation of an SMTP server object. +The goal of a single unit test is to test **only** some permutation of the +"unit under test". If you write a unit test that aims to verify the result +of a particular codepath through a Python function, you need only be +concerned about testing the code that *lives in the function body itself*. +If the function accepts a parameter that represents a complex application +"domain object" (such as a resource, a database connection, or an SMTP +server), the argument provided to this function during a unit test *need not +be* and likely *should not be* a "real" implementation object. For example, +although a particular function implementation may accept an argument that +represents an SMTP server object, and the function may call a method of this +object when the system is operating normally that would result in an email +being sent, a unit test of this codepath of the function does *not* need to +test that an email is actually sent. It just needs to make sure that the +function calls the method of the object provided as an argument that *would* +send an email if the argument happened to be the "real" implementation of an +SMTP server object. An *integration test*, on the other hand, is a different form of testing in which the interaction between two or more "units" is @@ -298,9 +297,9 @@ sure to use this pattern in your test case's ``setUp`` and See the :ref:`testing_module` chapter for the entire :app:`Pyramid` -specific testing API. This chapter describes APIs for registering a -security policy, registering models at paths, registering event +security policy, registering resources at paths, registering event listeners, registering views and view permissions, and classes -representing "dummy" implementations of a request and a model. +representing "dummy" implementations of a request and a resource. See also the various methods of the :term:`Configurator` documented in :ref:`configuration_module` that begin with the ``testing_`` prefix. diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index f0ace76ba..8adeb2511 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -375,7 +375,7 @@ order of route configuration declarations is very important. The order that routes declarations are evaluated is the order in which they are added to the application at startup time. This is unlike :term:`traversal`, which depends on emergent behavior which happens as -a result of traversing a graph. +a result of traversing a resource tree. For routes added via the :mod:`pyramid.config.Configurator.add_route` method, the order that routes are evaluated is the order in which they are @@ -414,7 +414,7 @@ found via :term:`view lookup`. :linenos: config.add_route('abc', '/abc', view='myproject.views.theview', - factory='myproject.models.root_factory') + factory='myproject.resources.root_factory') The factory can either be a Python object or a :term:`dotted Python name` (a string) which points to such a Python object, as it is above. @@ -459,7 +459,7 @@ represent neither predicates nor view configuration information. A Python object (often a function or a class) or a :term:`dotted Python name` to such an object that will generate a :app:`Pyramid` :term:`context` object when this route - matches. For example, ``mypackage.models.MyFactoryClass``. If this + matches. For example, ``mypackage.resources.MyFactoryClass``. If this argument is not specified, the traversal root factory will be used. ``traverse`` @@ -979,10 +979,10 @@ An example of using a route with a factory: config.add_route('idea', 'ideas/{idea}', view='myproject.views.idea_view', - factory='myproject.models.Idea') + factory='myproject.resources.Idea') -The above route will manufacture an ``Idea`` model as a -:term:`context`, assuming that ``mypackage.models.Idea`` resolves to a +The above route will manufacture an ``Idea`` resource as a +:term:`context`, assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a request in its ``__init__``. For example: .. code-block:: python diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index d1fd1b382..65168806e 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -85,21 +85,19 @@ Virtual Root Support :term:`traversal` -based (but not :term:`URL dispatch` -based) applications. -Virtual root support is useful when you'd like to host some model in a -:app:`Pyramid` object graph as an application under a URL pathname -that does not include the model path itself. For example, you might -want to serve the object at the traversal path ``/cms`` as an -application reachable via ``http://example.com/`` (as opposed to -``http://example.com/cms``). - -To specify a virtual root, cause an environment variable to be -inserted into the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value -that is the absolute pathname to the model object in the traversal -graph that should behave as the "root" model. As a result, the -traversal machinery will respect this value during traversal -(prepending it to the PATH_INFO before traversal starts), and the -:func:`pyramid.url.model_url` API will generate the "correct" -virtually-rooted URLs. +Virtual root support is useful when you'd like to host some resource in a +:app:`Pyramid` resource tree as an application under a URL pathname that does +not include the resource path itself. For example, you might want to serve the +object at the traversal path ``/cms`` as an application reachable via +``http://example.com/`` (as opposed to ``http://example.com/cms``). + +To specify a virtual root, cause an environment variable to be inserted into +the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value that is the absolute +pathname to the resource object in the resource tree that should behave as +the "root" resource. As a result, the traversal machinery will respect this +value during traversal (prepending it to the PATH_INFO before traversal +starts), and the :func:`pyramid.url.resource_url` API will generate the +"correct" virtually-rooted URLs. An example of an Apache ``mod_proxy`` configuration that will host the ``/cms`` subobject as ``http://www.example.com/`` using this facility diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 8a689be21..dccbc6b5a 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -3,11 +3,10 @@ Views ===== -The primary job of any :app:`Pyramid` application is is to find and -invoke a :term:`view callable` when a :term:`request` reaches the -application. View callables are bits of code written by you -- the -application developer -- which do something interesting in response to -a request made to your application. +The primary job of any :app:`Pyramid` application is is to find and invoke a +:term:`view callable` when a :term:`request` reaches the application. View +callables are bits of code which do something interesting in response to a +request made to your application. .. note:: @@ -18,11 +17,10 @@ a request made to your application. that implements a view *callable*, and the process of view *lookup*. -The chapter :ref:`contextfinding_chapter` describes how, using -information from the :term:`request`, a :term:`context` and a -:term:`view name` are computed. But neither the context nor the view -name found are very useful unless those elements can eventually be -mapped to a :term:`view callable`. +The chapter :ref:`contextfinding_chapter` describes how, using information +from the :term:`request`, a :term:`context` and a :term:`view name` are +computed. But neither the context nor the view name found are very useful +unless those elements can eventually be mapped to a :term:`view callable`. The job of actually locating and invoking the "best" :term:`view callable` is the job of the :term:`view lookup` subsystem. The view @@ -139,9 +137,8 @@ represent the method expected to return a response, you can use an function defined in this style can be defined as follows: context - An instance of a :term:`context` found via graph :term:`traversal` - or :term:`URL dispatch`. If the context is found via traversal, it - will be a :term:`model` object. + The :term:`resource` object found via tree :term:`traversal` + or :term:`URL dispatch`. request A :app:`Pyramid` Request object representing the current WSGI @@ -476,7 +473,7 @@ You can configure a view to use the JSON renderer by naming ``json`` as the config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='json') @@ -494,22 +491,21 @@ attributes by attaching properties to the request. See Two built-in renderers exist for :term:`Chameleon` templates. -If the ``renderer`` attribute of a view configuration is an absolute -path, a relative path or :term:`resource specification` which has a -final path element with a filename extension of ``.pt``, the Chameleon -ZPT renderer is used. See :ref:`chameleon_zpt_templates` for more -information about ZPT templates. +If the ``renderer`` attribute of a view configuration is an absolute path, a +relative path or :term:`asset specification` which has a final path element +with a filename extension of ``.pt``, the Chameleon ZPT renderer is used. +See :ref:`chameleon_zpt_templates` for more information about ZPT templates. If the ``renderer`` attribute of a view configuration is an absolute path or -a :term:`resource specification` which has a final path element with a -filename extension of ``.txt``, the :term:`Chameleon` text renderer is used. -See :ref:`chameleon_zpt_templates` for more information about Chameleon text +a :term:`asset specification` which has a final path element with a filename +extension of ``.txt``, the :term:`Chameleon` text renderer is used. See +:ref:`chameleon_zpt_templates` for more information about Chameleon text templates. The behavior of these renderers is the same, except for the engine used to render the template. -When a ``renderer`` attribute that names a template path or :term:`resource +When a ``renderer`` attribute that names a template path or :term:`asset specification` (e.g. ``myproject:templates/foo.pt`` or ``myproject:templates/foo.txt``) is used, the view must return a :term:`Response` object or a Python *dictionary*. If the view callable with @@ -541,7 +537,7 @@ renderer: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='myproject:templates/foo.pt') Here's an example view configuration which uses a Chameleon text @@ -552,7 +548,7 @@ renderer: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='myproject:templates/foo.txt') Views which use a Chameleon renderer can vary response attributes by @@ -573,13 +569,13 @@ The dictionary items will then be used in the global template space. If the view callable returns anything but a Response object, or a dictionary, an error will be raised. -When using a ``renderer`` argument to a :term:`view configuration` to -specify a Mako template, the value of the ``renderer`` may be a path -relative to the ``mako.directories`` setting (e.g. -``some/template.mak``) or, alternately, it may be a :term:`resource -specification` (e.g. ``apackage:templates/sometemplate.mak``). Mako -templates may internally inherit other Mako templates using a relative -filename or a :term:`resource specification` as desired. +When using a ``renderer`` argument to a :term:`view configuration` to specify +a Mako template, the value of the ``renderer`` may be a path relative to the +``mako.directories`` setting (e.g. ``some/template.mak``) or, alternately, +it may be a :term:`asset specification` +(e.g. ``apackage:templates/sometemplate.mak``). Mako templates may +internally inherit other Mako templates using a relative filename or a +:term:`asset specification` as desired. XXX Further explanation or link to mako inheritance info @@ -592,7 +588,7 @@ Here's an example view configuration which uses a relative path: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='foo.mak') It's important to note that in Mako's case, the 'relative' path name @@ -600,16 +596,15 @@ It's important to note that in Mako's case, the 'relative' path name directory (or directories) configured for Mako via the ``mako.directories`` configuration file setting. -The renderer can also be provided in :term:`resource specification` -format. Here's an example view configuration which uses a :term:`resource -specification`: +The renderer can also be provided in :term:`asset specification` +format. Here's an example view configuration which uses one: .. code-block:: python :linenos: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='mypackage:templates/foo.mak') The above configuration will use the file named ``foo.mak`` in the @@ -746,26 +741,27 @@ factory constructor is available as :class:`pyramid.interfaces.IRendererInfo`. There are essentially two different kinds of renderer factories: -- A renderer factory which expects to accept a :term:`resource specification`, - or an absolute path, as the ``name`` attribute of the ``info`` object fed to - its constructor. These renderer factories are registered with a ``name`` - value that begins with a dot (``.``). These types of renderer factories - usually relate to a file on the filesystem, such as a template. - -- A renderer factory which expects to accept a token that does not represent a - filesystem path or a resource specification in the ``name`` attribute of the +- A renderer factory which expects to accept a :term:`asset + specification`, or an absolute path, as the ``name`` attribute of the ``info`` object fed to its constructor. These renderer factories are - registered with a ``name`` value that does not begin with a dot. These - renderer factories are typically object serializers. + registered with a ``name`` value that begins with a dot (``.``). These + types of renderer factories usually relate to a file on the filesystem, + such as a template. + +- A renderer factory which expects to accept a token that does not represent + a filesystem path or a asset specification in the ``name`` + attribute of the ``info`` object fed to its constructor. These renderer + factories are registered with a ``name`` value that does not begin with a + dot. These renderer factories are typically object serializers. -.. sidebar:: Resource Specifications +.. sidebar:: Asset Specifications - A resource specification is a colon-delimited identifier for a - :term:`resource`. The colon separates a Python :term:`package` - name from a package subpath. For example, the resource - specification ``my.package:static/baz.css`` identifies the file - named ``baz.css`` in the ``static`` subdirectory of the - ``my.package`` Python :term:`package`. + A asset specification is a colon-delimited identifier for a + :term:`asset`. The colon separates a Python :term:`package` + name from a package subpath. For example, the asset + specification ``my.package:static/baz.css`` identifies the file named + ``baz.css`` in the ``static`` subdirectory of the ``my.package`` Python + :term:`package`. Here's an example of the registration of a simple renderer factory via :meth:`pyramid.config.Configurator.add_renderer`: @@ -1211,30 +1207,26 @@ Non-Predicate Arguments itself if the view is a function, or the ``__call__`` callable attribute if the view is a class). -``renderer`` - This is either a single string term (e.g. ``json``) or a string - implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``) naming a :term:`renderer` - implementation. If the ``renderer`` value does not contain a dot - (``.``), the specified string will be used to look up a renderer - implementation, and that renderer implementation will be used to - construct a response from the view return value. If the - ``renderer`` value contains a dot (``.``), the specified term will - be treated as a path, and the filename extension of the last element - in the path will be used to look up the renderer implementation, - which will be passed the full path. The renderer implementation - will be used to construct a :term:`response` from the view return - value. - - When the renderer is a path, although a path is usually just a - simple relative pathname (e.g. ``templates/foo.pt``, implying that a - template named "foo.pt" is in the "templates" directory relative to - the directory of the current :term:`package`), a path can be - absolute, starting with a slash on UNIX or a drive letter prefix on - Windows. The path can alternately be a :term:`resource - specification` in the form - ``some.dotted.package_name:relative/path``, making it possible to - address template resources which live in a separate package. +``renderer`` This is either a single string term (e.g. ``json``) or a string + implying a path or :term:`asset specification` + (e.g. ``templates/views.pt``) naming a :term:`renderer` implementation. If + the ``renderer`` value does not contain a dot (``.``), the specified string + will be used to look up a renderer implementation, and that renderer + implementation will be used to construct a response from the view return + value. If the ``renderer`` value contains a dot (``.``), the specified + term will be treated as a path, and the filename extension of the last + element in the path will be used to look up the renderer implementation, + which will be passed the full path. The renderer implementation will be + used to construct a :term:`response` from the view return value. + + When the renderer is a path, although a path is usually just a simple + relative pathname (e.g. ``templates/foo.pt``, implying that a template + named "foo.pt" is in the "templates" directory relative to the directory of + the current :term:`package`), a path can be absolute, starting with a slash + on UNIX or a drive letter prefix on Windows. The path can alternately be a + :term:`asset specification` in the form + ``some.dotted.package_name:relative/path``, making it possible to address + template assets which live in a separate package. The ``renderer`` attribute is optional. If it is not defined, the "null" renderer is assumed (no rendering is performed and the value @@ -1276,15 +1268,15 @@ the usage of the configured view. default view). ``context`` - An object representing a Python class that the :term:`context` must be - an instance of, *or* the :term:`interface` that the :term:`context` - must provide in order for this view to be found and called. This - predicate is true when the :term:`context` is an instance of the - represented class or if the :term:`context` provides the represented - interface; it is otherwise false. + An object representing a Python class that the :term:`context` resource + must be an instance of, *or* the :term:`interface` that the :term:`context` + must provide in order for this view to be found and called. This predicate + is true when the :term:`context` is an instance of the represented class or + if the :term:`context` provides the represented interface; it is otherwise + false. - If ``context`` is not supplied, the value ``None``, which matches - any model, is used. + If ``context`` is not supplied, the value ``None``, which matches any + resource, is used. ``route_name`` If ``route_name`` is supplied, the view callable will be invoked @@ -1344,7 +1336,7 @@ the usage of the configured view. This value should be a reference to a Python class or :term:`interface` that a parent object in the context's :term:`lineage` must provide in order for this view to be found and - called. The nodes in your object graph must be "location-aware" to + called. The resources in your resource tree must be "location-aware" to use this feature. If ``containment`` is not supplied, the interfaces and classes in @@ -1469,11 +1461,11 @@ reside in a :app:`Pyramid` application module ``views.py``: .. code-block:: python :linenos: - from models import MyModel + from resources import MyResource from pyramid.view import view_config from pyramid.chameleon_zpt import render_template_to_response - @view_config(name='my_view', request_method='POST', context=MyModel, + @view_config(name='my_view', request_method='POST', context=MyResource, permission='read', renderer='templates/my.pt') def my_view(request): return {'a':1} @@ -1486,7 +1478,7 @@ configuration stanza: :linenos: config.add_view('.views.my_view', name='my_view', request_method='POST', - context=MyModel, permission='read') + context=MyResource, permission='read') All arguments to ``view_config`` may be omitted. For example: @@ -1503,7 +1495,7 @@ All arguments to ``view_config`` may be omitted. For example: Such a registration as the one directly above implies that the view name will be ``my_view``, registered with a ``context`` argument that -matches any model type, using no permission, registered against +matches any resource type, using no permission, registered against requests with any request method, request type, request param, route name, or containment. @@ -1686,32 +1678,32 @@ such an object. All other arguments are optional. See information. .. index:: - single: model interfaces + single: resource interfaces -.. _using_model_interfaces: +.. _using_resource_interfaces: -Using Model Interfaces In View Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using Resource Interfaces In View Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of registering your views with a ``context`` that names a -Python model *class*, you can optionally register a view callable with -a ``context`` which is an :term:`interface`. An interface can be -attached arbitrarily to any model instance. View lookup treats -context interfaces specially, and therefore the identity of a model -can be divorced from that of the class which implements it. As a -result, associating a view with an interface can provide more -flexibility for sharing a single view between two or more different -implementations of a model type. For example, if two model object -instances of different Python class types share the same interface, -you can use the same view against each of them. +Instead of registering your views with a ``context`` that names a Python +resource *class*, you can optionally register a view callable with a +``context`` which is an :term:`interface`. An interface can be attached +arbitrarily to any resource object. View lookup treats context interfaces +specially, and therefore the identity of a resource can be divorced from that +of the class which implements it. As a result, associating a view with an +interface can provide more flexibility for sharing a single view between two +or more different implementations of a resource type. For example, if two +resource objects of different Python class types share the same interface, +you can use the same view configuration to specify both of them as a +``context``. -In order to make use of interfaces in your application during view -dispatch, you must create an interface and mark up your model classes -or instances with interface declarations that refer to this interface. +In order to make use of interfaces in your application during view dispatch, +you must create an interface and mark up your resource classes or instances +with interface declarations that refer to this interface. -To attach an interface to a model *class*, you define the interface -and use the :func:`zope.interface.implements` function to associate -the interface with the class. +To attach an interface to a resource *class*, you define the interface and +use the :func:`zope.interface.implements` function to associate the interface +with the class. .. code-block:: python :linenos: @@ -1725,10 +1717,10 @@ the interface with the class. class Hello(object): implements(IHello) -To attach an interface to a model *instance*, you define the interface -and use the :func:`zope.interface.alsoProvides` function to associate -the interface with the instance. This function mutates the instance -in such a way that the interface is attached to it. +To attach an interface to a resource *instance*, you define the interface and +use the :func:`zope.interface.alsoProvides` function to associate the +interface with the instance. This function mutates the instance in such a +way that the interface is attached to it. .. code-block:: python :linenos: @@ -1747,13 +1739,13 @@ in such a way that the interface is attached to it. alsoProvides(hello, IHello) return hello -Regardless of how you associate an interface, with a model instance, or a model -class, the resulting code to associate that interface with a view callable is -the same. Assuming the above code that defines an ``IHello`` interface lives -in the root of your application, and its module is named "models.py", the -interface declaration below will associate the -``mypackage.views.hello_world`` view with models that implement, or provide, -this interface. +Regardless of how you associate an interface, with a resource instance, or a +resource class, the resulting code to associate that interface with a view +callable is the same. Assuming the above code that defines an ``IHello`` +interface lives in the root of your application, and its module is named +"resources.py", the interface declaration below will associate the +``mypackage.views.hello_world`` view with resources that implement, or +provide, this interface. .. code-block:: python :linenos: @@ -1761,22 +1753,25 @@ this interface. # config is an instance of pyramid.config.Configurator config.add_view('mypackage.views.hello_world', name='hello.html', - context='mypackage.models.IHello') + context='mypackage.resources.IHello') -Any time a model that is determined to be the :term:`context` provides this -interface, and a view named ``hello.html`` is looked up against it as per the -URL, the ``mypackage.views.hello_world`` view callable will be invoked. +Any time a resource that is determined to be the :term:`context` provides +this interface, and a view named ``hello.html`` is looked up against it as +per the URL, the ``mypackage.views.hello_world`` view callable will be +invoked. -Note, in cases where a view is registered against a model class, and a -view is also registered against an interface that the model class -implements, an ambiguity arises. Views registered for the model class -take precedence over any views registered for any interface the model -class implements. Thus, if a view is registered for both the class type -of the context and an interface implemented by the context's class, the -view registered for the context's class will "win". +Note, in cases where a view is registered against a resource class, and a +view is also registered against an interface that the resource class +implements, an ambiguity arises. Views registered for the resource class take +precedence over any views registered for any interface the resource class +implements. Thus, if one view configuration names a ``context`` of both the +class type of a resource, and another view configuration names a ``context`` +of interface implemented by the resource's class, and both view +configurations are otherwise identical, the view registered for the context's +class will "win". -For more information about defining models with interfaces for use within -view configuration, see :ref:`models_which_implement_interfaces`. +For more information about defining resources with interfaces for use within +view configuration, see :ref:`resources_which_implement_interfaces`. .. index:: single: view security @@ -1787,22 +1782,12 @@ view configuration, see :ref:`models_which_implement_interfaces`. Configuring View Security ~~~~~~~~~~~~~~~~~~~~~~~~~ -<<<<<<< HEAD -If an :term:`authorization policy` is active, any :term:`permission` -attached to a :term:`view configuration` found during view lookup will -be verified. This will ensure that the currently authenticated user -possesses that permission against the :term:`context` before the view -function is actually called. Here's an example of specifying a -permission in a view configuration using -:meth:`pyramid.configuration.Configurator.add_view`: -======= If a :term:`authorization policy` is active, any :term:`permission` attached -to a :term:`view configuration` found during view lookup will be consulted to -ensure that the currently authenticated user possesses that permission -against the :term:`context` before the view function is actually called. -Here's an example of specifying a permission in a view configuration using -:meth:`pyramid.config.Configurator.add_view`: ->>>>>>> fee38663daccc0130d0c34dbc5a14e67bef2e183 +to a :term:`view configuration` found during view lookup will be verified. +This will ensure that the currently authenticated user possesses that +permission against the :term:`context` before the view function is actually +called. Here's an example of specifying a permission in a view configuration +using :meth:`pyramid.config.Configurator.add_view`: .. code-block:: python :linenos: @@ -1810,7 +1795,7 @@ Here's an example of specifying a permission in a view configuration using # config is an instance of pyramid.config.Configurator config.add_view('myproject.views.add_entry', name='add.html', - context='myproject.models.IBlog', permission='add') + context='myproject.resources.IBlog', permission='add') When an :term:`authorization policy` is enabled, this view will be protected with the ``add`` permission. The view will *not be called* if diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst index c930ecc50..f330fd551 100644 --- a/docs/narr/zca.rst +++ b/docs/narr/zca.rst @@ -129,7 +129,7 @@ interface of a registry instead, you need only know how to obtain the There are two ways of doing so: - use the :func:`pyramid.threadlocal.get_current_registry` - function within :app:`Pyramid` view or model code. This will + function within :app:`Pyramid` view or resource code. This will always return the "current" :app:`Pyramid` application registry. - use the attribute of the :term:`request` object named ``registry`` diff --git a/pyramid/config.py b/pyramid/config.py index 0932ae7b8..2e64d49e9 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -1858,18 +1858,18 @@ class Configurator(object): self.action((IRendererFactory, name), None) @action_method - def override_resource(self, to_override, override_with, _override=None): - """ Add a :app:`Pyramid` resource override to the current + def override_asset(self, to_override, override_with, _override=None): + """ Add a :app:`Pyramid` asset override to the current configuration state. - ``to_override`` is a :term:`resource specification` to the - resource being overridden. + ``to_override`` is a :term:`asset specification` to the + asset being overridden. - ``override_with`` is a :term:`resource specification` to the - resource that is performing the override. + ``override_with`` is a :term:`asset specification` to the + asset that is performing the override. - See :ref:`resources_chapter` for more - information about resource overrides.""" + See :ref:`assets_chapter` for more + information about asset overrides.""" if to_override == override_with: raise ConfigurationError('You cannot override a resource with ' 'itself') @@ -1905,6 +1905,8 @@ class Configurator(object): override(from_package, path, to_package, override_prefix) self.action(None, register) + override_resource = override_asset # bw compat + @action_method def set_forbidden_view(self, view=None, attr=None, renderer=None, wrapper=None): diff --git a/pyramid/includes/meta.zcml b/pyramid/includes/meta.zcml index 58af814ef..864cff564 100644 --- a/pyramid/includes/meta.zcml +++ b/pyramid/includes/meta.zcml @@ -40,9 +40,15 @@ handler="pyramid.zcml.handler" /> + + diff --git a/pyramid/request.py b/pyramid/request.py index 6c9a1e421..302685d7e 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -8,7 +8,7 @@ from pyramid.interfaces import ISessionFactory from pyramid.exceptions import ConfigurationError from pyramid.decorator import reify -from pyramid.url import model_url +from pyramid.url import resource_url from pyramid.url import route_url from pyramid.url import static_url from pyramid.url import route_path @@ -195,32 +195,34 @@ class Request(WebobRequest): """ return route_url(route_name, self, *elements, **kw) - def model_url(self, model, *elements, **kw): - """ Return the URL for the model object named ``model``, using - ``*elements`` and ``**kw`` as modifiers. + def resource_url(self, resource, *elements, **kw): + """ Return the URL for the :term:`resource` object named ``resource``, + using ``*elements`` and ``**kw`` as modifiers. This is a convenience method. The result of calling - :meth:`pyramid.request.Request.model_url` is the same as calling - :func:`pyramid.url.model_url` with an explicit ``request`` parameter. - - The :meth:`pyramid.request.Request.model_url` method calls the - :func:`pyramid.url.model_url` function using the Request object as - the ``request`` argument. The ``model``, ``*elements`` and ``*kw`` - arguments passed to :meth:`pyramid.request.Request.model_url` are - passed through to :func:`pyramid.url.model_url` unchanged and its + :meth:`pyramid.request.Request.resource_url` is the same as calling + :func:`pyramid.url.resource_url` with an explicit ``request`` parameter. + + The :meth:`pyramid.request.Request.resource_url` method calls the + :func:`pyramid.url.resource_url` function using the Request object as + the ``request`` argument. The ``resource``, ``*elements`` and ``*kw`` + arguments passed to :meth:`pyramid.request.Request.resource_url` are + passed through to :func:`pyramid.url.resource_url` unchanged and its result is returned. - This call to :meth:`pyramid.request.Request.model_url`:: + This call to :meth:`pyramid.request.Request.resource_url`:: - request.model_url(mymodel) + request.resource_url(myresource) - Is completely equivalent to calling :func:`pyramid.url.model_url` + Is completely equivalent to calling :func:`pyramid.url.resource_url` like this:: from pyramid.url import model_url - model_url(model, request) + resource_url(resource, request) """ - return model_url(model, self, *elements, **kw) + return resource_url(resource, self, *elements, **kw) + + model_url = resource_url # b/w compat forever def static_url(self, path, **kw): """ Generates a fully qualified URL for a static :term:`resource`. diff --git a/pyramid/traversal.py b/pyramid/traversal.py index fb73ad906..404e82ecb 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -15,31 +15,31 @@ from pyramid.exceptions import URLDecodeError from pyramid.location import lineage from pyramid.threadlocal import get_current_registry -def find_root(model): - """ Find the root node in the graph to which ``model`` - belongs. Note that ``model`` should be :term:`location`-aware. - Note that the root node is available in the request object by +def find_root(resource): + """ Find the root node in the graph to which ``resource`` + belongs. Note that ``resource`` should be :term:`location`-aware. + Note that the root resource is available in the request object by accessing the ``request.root`` attribute. """ - for location in lineage(model): + for location in lineage(resource): if location.__parent__ is None: - model = location + resource = location break - return model + return resource -def find_model(model, path): - """ Given a model object and a string or tuple representing a path +def find_resource(resource, path): + """ Given a resource object and a string or tuple representing a path (such as the return value of - :func:`pyramid.traversal.model_path` or - :func:`pyramid.traversal.model_path_tuple`), return a context - in this application's model graph at the specified path. The - model passed in *must* be :term:`location`-aware. If the path + :func:`pyramid.traversal.resource_path` or + :func:`pyramid.traversal.resource_path_tuple`), return a context + in this application's resource tree at the specified path. The + resource passed in *must* be :term:`location`-aware. If the path cannot be resolved (if the respective node in the graph does not exist), a :exc:`KeyError` will be raised. This function is the logical inverse of - :func:`pyramid.traversal.model_path` and - :func:`pyramid.traversal.model_path_tuple`; it can resolve any + :func:`pyramid.traversal.resource_path` and + :func:`pyramid.traversal.resource_path_tuple`; it can resolve any path string or tuple generated by either of those functions. Rules for passing a *string* as the ``path`` argument: if the @@ -47,15 +47,15 @@ def find_model(model, path): character, the path will considered absolute and the graph traversal will start at the root object. If the first character of the path string is *not* the ``/`` character, the path is - considered relative and graph traversal will begin at the model - object supplied to the function as the ``model`` argument. If an - empty string is passed as ``path``, the ``model`` passed in will - be returned. Model path strings must be escaped in the following + considered relative and graph traversal will begin at the resource + object supplied to the function as the ``resource`` argument. If an + empty string is passed as ``path``, the ``resource`` passed in will + be returned. Resource path strings must be escaped in the following manner: each Unicode path segment must be encoded as UTF-8 and as each path segment must escaped via Python's :mod:`urllib.quote`. For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or ``to%20the/La%20Pe%C3%B1a`` (relative). The - :func:`pyramid.traversal.model_path` function generates strings + :func:`pyramid.traversal.resource_path` function generates strings which follow these rules (albeit only absolute ones). Rules for passing a *tuple* as the ``path`` argument: if the first @@ -64,108 +64,111 @@ def find_model(model, path): traversal will start at the graph root object. If the first element in the path tuple is not the empty string (for example ``('a', 'b', 'c')``), the path is considered relative and graph - traversal will begin at the model object supplied to the function - as the ``model`` argument. If an empty sequence is passed as - ``path``, the ``model`` passed in itself will be returned. No + traversal will begin at the resource object supplied to the function + as the ``resource`` argument. If an empty sequence is passed as + ``path``, the ``resource`` passed in itself will be returned. No URL-quoting or UTF-8-encoding of individual path segments within the tuple is required (each segment may be any string or unicode - object representing a model name). Model path tuples generated by - :func:`pyramid.traversal.model_path_tuple` can always be - resolved by ``find_model``. + object representing a resource name). Resource path tuples generated by + :func:`pyramid.traversal.resource_path_tuple` can always be + resolved by ``find_resource``. """ - D = traverse(model, path) + D = traverse(resource, path) view_name = D['view_name'] context = D['context'] if view_name: raise KeyError('%r has no subelement %s' % (context, view_name)) return context -def find_interface(model, class_or_interface): +find_model = find_resource # b/w compat + +def find_interface(resource, class_or_interface): """ - Return the first object found in the parent chain of ``model`` + Return the first object found in the parent chain of ``resource`` which, a) if ``class_or_interface`` is a Python class object, is an instance of the class or any subclass of that class or b) if ``class_or_interface`` is a :term:`interface`, provides the specified interface. Return ``None`` if no object providing ``interface_or_class`` can be found in the parent chain. The - ``model`` passed in *must* be :term:`location`-aware. + ``resource`` passed in *must* be :term:`location`-aware. """ if IInterface.providedBy(class_or_interface): test = class_or_interface.providedBy else: test = lambda arg: isinstance(arg, class_or_interface) - for location in lineage(model): + for location in lineage(resource): if test(location): return location -def model_path(model, *elements): - """ Return a string object representing the absolute physical path - of the model object based on its position in the model graph, e.g - ``/foo/bar``. Any positional arguments passed in as ``elements`` - will be appended as path segments to the end of the model path. - For instance, if the model's path is ``/foo/bar`` and ``elements`` - equals ``('a', 'b')``, the returned string will be - ``/foo/bar/a/b``. The first character in the string will always - be the ``/`` character (a leading ``/`` character in a path string - represents that the path is absolute). - - Model path strings returned will be escaped in the following +def resource_path(resource, *elements): + """ Return a string object representing the absolute physical path of the + resource object based on its position in the resource tree, e.g + ``/foo/bar``. Any positional arguments passed in as ``elements`` will be + appended as path segments to the end of the resource path. For instance, + if the resource's path is ``/foo/bar`` and ``elements`` equals ``('a', + 'b')``, the returned string will be ``/foo/bar/a/b``. The first + character in the string will always be the ``/`` character (a leading + ``/`` character in a path string represents that the path is absolute). + + Resource path strings returned will be escaped in the following manner: each unicode path segment will be encoded as UTF-8 and each path segment will be escaped via Python's :mod:`urllib.quote`. For example, ``/path/to%20the/La%20Pe%C3%B1a``. This function is a logical inverse of - :mod:`pyramid.traversal.find_model`: it can be used to generate + :mod:`pyramid.traversal.find_resource`: it can be used to generate path references that can later be resolved via that function. - The ``model`` passed in *must* be :term:`location`-aware. + The ``resource`` passed in *must* be :term:`location`-aware. .. note:: Each segment in the path string returned will use the - ``__name__`` attribute of the model it represents within + ``__name__`` attribute of the resource it represents within the graph. Each of these segments *should* be a unicode or string object (as per the contract of :term:`location`-awareness). However, no conversion or - safety checking of model names is performed. For - instance, if one of the models in your graph has a + safety checking of resource names is performed. For + instance, if one of the resources in your tree has a ``__name__`` which (by error) is a dictionary, the - :func:`pyramid.traversal.model_path` function will + :func:`pyramid.traversal.resource_path` function will attempt to append it to a string and it will cause a :exc:`pyramid.exceptions.URLDecodeError`. - .. note:: The :term:`root` model *must* have a ``__name__`` + .. note:: The :term:`root` resource *must* have a ``__name__`` attribute with a value of either ``None`` or the empty string for paths to be generated properly. If the root - model has a non-null ``__name__`` attribute, its name + resource has a non-null ``__name__`` attribute, its name will be prepended to the generated path rather than a single leading '/' character. """ # joining strings is a bit expensive so we delegate to a function # which caches the joined result for us - return _join_path_tuple(model_path_tuple(model, *elements)) + return _join_path_tuple(resource_path_tuple(resource, *elements)) + +model_path = resource_path # b/w compat -def traverse(model, path): - """Given a model object as ``model`` and a string or tuple +def traverse(resource, path): + """Given a resource object as ``resource`` and a string or tuple representing a path as ``path`` (such as the return value of - :func:`pyramid.traversal.model_path` or - :func:`pyramid.traversal.model_path_tuple` or the value of + :func:`pyramid.traversal.resource_path` or + :func:`pyramid.traversal.resource_path_tuple` or the value of ``request.environ['PATH_INFO']``), return a dictionary with the keys ``context``, ``root``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. A definition of each value in the returned dictionary: - - ``context``: The :term:`context` (a :term:`model` object) found + - ``context``: The :term:`context` (a :term:`resource` object) found via traversal or url dispatch. If the ``path`` passed in is the - empty string, the value of the ``model`` argument passed to this + empty string, the value of the ``resource`` argument passed to this function is returned. - - ``root``: The model object at which :term:`traversal` begins. - If the ``model`` passed in was found via url dispatch or if the + - ``root``: The resource object at which :term:`traversal` begins. + If the ``resource`` passed in was found via url dispatch or if the ``path`` passed in was relative (non-absolute), the value of the - ``model`` argument passed to this function is returned. + ``resource`` argument passed to this function is returned. - ``view_name``: The :term:`view name` found during - :term:`traversal` or :term:`url dispatch`; if the ``model`` was + :term:`traversal` or :term:`url dispatch`; if the ``resource`` was found via traversal, this is usually a representation of the path segment which directly follows the path to the ``context`` in the ``path``. The ``view_name`` will be a Unicode object or @@ -173,11 +176,11 @@ def traverse(model, path): there is no element which follows the ``context`` path. An example: if the path passed is ``/foo/bar``, and a context object is found at ``/foo`` (but not at ``/foo/bar``), the 'view - name' will be ``u'bar'``. If the ``model`` was found via + name' will be ``u'bar'``. If the ``resource`` was found via urldispatch, the view_name will be the name the route found was registered with. - - ``subpath``: For a ``model`` found via :term:`traversal`, this + - ``subpath``: For a ``resource`` found via :term:`traversal`, this is a sequence of path segments found in the ``path`` that follow the ``view_name`` (if any). Each of these items is a Unicode object. If no path segments follow the ``view_name``, the @@ -185,7 +188,7 @@ def traverse(model, path): passed is ``/foo/bar/baz/buz``, and a context object is found at ``/foo`` (but not ``/foo/bar``), the 'view name' will be ``u'bar'`` and the :term:`subpath` will be ``[u'baz', u'buz']``. - For a ``model`` found via url dispatch, the subpath will be a + For a ``resource`` found via url dispatch, the subpath will be a sequence of values discerned from ``*subpath`` in the route pattern matched or the empty sequence. @@ -194,27 +197,27 @@ def traverse(model, path): Each of these items is a Unicode object. If no path segments were traversed to find the ``context`` object (e.g. if the ``path`` provided is the empty string), the ``traversed`` value - will be the empty sequence. If the ``model`` is a model found + will be the empty sequence. If the ``resource`` is a resource found via :term:`url dispatch`, traversed will be None. - - ``virtual_root``: A model object representing the 'virtual' root + - ``virtual_root``: A resource object representing the 'virtual' root of the object graph being traversed during :term:`traversal`. See :ref:`vhosting_chapter` for a definition of the virtual root object. If no virtual hosting is in effect, and the ``path`` passed in was absolute, the ``virtual_root`` will be the *physical* root object (the object at which :term:`traversal` - begins). If the ``model`` passed in was found via :term:`URL + begins). If the ``resource`` passed in was found via :term:`URL dispatch` or if the ``path`` passed in was relative, the ``virtual_root`` will always equal the ``root`` object (the - model passed in). + resource passed in). - ``virtual_root_path`` -- If :term:`traversal` was used to find - the ``model``, this will be the sequence of path elements + the ``resource``, this will be the sequence of path elements traversed to find the ``virtual_root`` object. Each of these items is a Unicode object. If no path segments were traversed to find the ``virtual_root`` object (e.g. if virtual hosting is not in effect), the ``traversed`` value will be the empty list. - If url dispatch was used to find the ``model``, this will be + If url dispatch was used to find the ``resource``, this will be ``None``. If the path cannot be resolved, a :exc:`KeyError` will be raised. @@ -224,15 +227,15 @@ def traverse(model, path): character, the path will considered absolute and the graph traversal will start at the root object. If the first character of the path string is *not* the ``/`` character, the path is - considered relative and graph traversal will begin at the model - object supplied to the function as the ``model`` argument. If an - empty string is passed as ``path``, the ``model`` passed in will - be returned. Model path strings must be escaped in the following + considered relative and graph traversal will begin at the resource + object supplied to the function as the ``resource`` argument. If an + empty string is passed as ``path``, the ``resource`` passed in will + be returned. Resource path strings must be escaped in the following manner: each Unicode path segment must be encoded as UTF-8 and each path segment must escaped via Python's :mod:`urllib.quote`. For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or ``to%20the/La%20Pe%C3%B1a`` (relative). The - :func:`pyramid.traversal.model_path` function generates strings + :func:`pyramid.traversal.resource_path` function generates strings which follow these rules (albeit only absolute ones). Rules for passing a *tuple* as the ``path`` argument: if the first @@ -241,12 +244,12 @@ def traverse(model, path): traversal will start at the graph root object. If the first element in the path tuple is not the empty string (for example ``('a', 'b', 'c')``), the path is considered relative and graph - traversal will begin at the model object supplied to the function - as the ``model`` argument. If an empty sequence is passed as - ``path``, the ``model`` passed in itself will be returned. No + traversal will begin at the resource object supplied to the function + as the ``resource`` argument. If an empty sequence is passed as + ``path``, the ``resource`` passed in itself will be returned. No URL-quoting or UTF-8-encoding of individual path segments within the tuple is required (each segment may be any string or unicode - object representing a model name). + object representing a resource name). Explanation of the conversion of ``path`` segment values to Unicode during traversal: Each segment is URL-unquoted, and @@ -284,7 +287,7 @@ def traverse(model, path): path = path.encode('ascii') if path and path[0] == '/': - model = find_root(model) + resource = find_root(resource) reg = get_current_registry() @@ -295,19 +298,19 @@ def traverse(model, path): request = request_factory.blank(path) request.registry = reg - traverser = reg.queryAdapter(model, ITraverser) + traverser = reg.queryAdapter(resource, ITraverser) if traverser is None: - traverser = ModelGraphTraverser(model) + traverser = ResourceTreeTraverser(resource) return traverser(request) -def model_path_tuple(model, *elements): +def resource_path_tuple(resource, *elements): """ Return a tuple representing the absolute physical path of the - ``model`` object based on its position in an object graph, e.g + ``resource`` object based on its position in an object graph, e.g ``('', 'foo', 'bar')``. Any positional arguments passed in as ``elements`` will be appended as elements in the tuple - representing the model path. For instance, if the model's + representing the resource path. For instance, if the resource's path is ``('', 'foo', 'bar')`` and elements equals ``('a', 'b')``, the returned tuple will be ``('', 'foo', 'bar', 'a', b')``. The first element of this tuple will always be the empty string (a @@ -315,52 +318,54 @@ def model_path_tuple(model, *elements): path is absolute). This function is a logical inverse of - :func:`pyramid.traversal.find_model`: it can be used to + :func:`pyramid.traversal.find_resource`: it can be used to generate path references that can later be resolved that function. - The ``model`` passed in *must* be :term:`location`-aware. + The ``resource`` passed in *must* be :term:`location`-aware. .. note:: Each segment in the path tuple returned will equal the - ``__name__`` attribute of the model it represents within + ``__name__`` attribute of the resource it represents within the graph. Each of these segments *should* be a unicode or string object (as per the contract of :term:`location`-awareness). However, no conversion or - safety checking of model names is performed. For - instance, if one of the models in your graph has a + safety checking of resource names is performed. For + instance, if one of the resources in your tree has a ``__name__`` which (by error) is a dictionary, that dictionary will be placed in the path tuple; no warning or error will be given. - .. note:: The :term:`root` model *must* have a ``__name__`` + .. note:: The :term:`root` resource *must* have a ``__name__`` attribute with a value of either ``None`` or the empty string for path tuples to be generated properly. If - the root model has a non-null ``__name__`` attribute, + the root resource has a non-null ``__name__`` attribute, its name will be the first element in the generated path tuple rather than the empty string. """ - return tuple(_model_path_list(model, *elements)) + return tuple(_resource_path_list(resource, *elements)) -def _model_path_list(model, *elements): - """ Implementation detail shared by model_path and model_path_tuple """ - path = [loc.__name__ or '' for loc in lineage(model)] +def _resource_path_list(resource, *elements): + """ Implementation detail shared by resource_path and resource_path_tuple""" + path = [loc.__name__ or '' for loc in lineage(resource)] path.reverse() path.extend(elements) return path -def virtual_root(model, request): +_model_path_list = _resource_path_list # b/w compat + +def virtual_root(resource, request): """ - Provided any :term:`model` and a :term:`request` object, return - the model object representing the :term:`virtual root` of the + Provided any :term:`resource` and a :term:`request` object, return + the resource object representing the :term:`virtual root` of the current :term:`request`. Using a virtual root in a :term:`traversal` -based :app:`Pyramid` application permits rooting, for example, the object at the traversal path ``/cms`` at ``http://example.com/`` instead of rooting it at ``http://example.com/cms/``. - If the ``model`` passed in is a context obtained via + If the ``resource`` passed in is a context obtained via :term:`traversal`, and if the ``HTTP_X_VHM_ROOT`` key is in the WSGI environment, the value of this key will be treated as a - 'virtual root path': the :func:`pyramid.traversal.find_model` + 'virtual root path': the :func:`pyramid.traversal.find_resource` API will be used to find the virtual root object using this path; if the object is found, it will be returned. If the ``HTTP_X_VHM_ROOT`` key is is not present in the WSGI environment, @@ -370,16 +375,16 @@ def virtual_root(model, request): :term:`URL dispatch`. Contexts obtained via URL dispatch don't really support being virtually rooted (each URL dispatch context is both its own physical and virtual root). However if this API - is called with a ``model`` argument which is a context obtained - via URL dispatch, the model passed in will be returned + is called with a ``resource`` argument which is a context obtained + via URL dispatch, the resource passed in will be returned unconditionally.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c - urlgenerator = reg.queryMultiAdapter((model, request), IContextURL) + urlgenerator = reg.queryMultiAdapter((resource, request), IContextURL) if urlgenerator is None: - urlgenerator = TraversalContextURL(model, request) + urlgenerator = TraversalContextURL(resource, request) return urlgenerator.virtual_root() @lru_cache(1000) @@ -438,13 +443,13 @@ def traversal_path(path): (u'archives', u'') .. note:: This function does not generate the same type of tuples - that :func:`pyramid.traversal.model_path_tuple` does. + that :func:`pyramid.traversal.resource_path_tuple` does. In particular, the leading empty string is not present in the tuple it returns, unlike tuples returned by - :func:`pyramid.traversal.model_path_tuple`. As a + :func:`pyramid.traversal.resource_path_tuple`. As a result, tuples generated by ``traversal_path`` are not resolveable by the - :func:`pyramid.traversal.find_model` API. + :func:`pyramid.traversal.find_resource` API. ``traversal_path`` is a function mostly used by the internals of :app:`Pyramid` and by people writing their own traversal machinery, as opposed to users @@ -474,7 +479,7 @@ _segment_cache = {} def quote_path_segment(segment): """ Return a quoted representation of a 'path segment' (such as - the string ``__name__`` attribute of a model) as a string. If the + the string ``__name__`` attribute of a resource) as a string. If the ``segment`` passed in is a unicode object, it is converted to a UTF-8 string, then it is URL-quoted using Python's ``urllib.quote``. If the ``segment`` passed in is a string, it is @@ -510,10 +515,10 @@ def quote_path_segment(segment): _segment_cache[segment] = result return result -class ModelGraphTraverser(object): - """ A model graph traverser that should be used (for speed) when - every object in the graph supplies a ``__name__`` and - ``__parent__`` attribute (ie. every object in the graph is +class ResourceTreeTraverser(object): + """ A resource tree traverser that should be used (for speed) when + every object in the tree supplies a ``__name__`` and + ``__parent__`` attribute (ie. every object in the tree is :term:`location` aware) .""" implements(ITraverser) @@ -616,9 +621,11 @@ class ModelGraphTraverser(object): 'traversed':vpath_tuple, 'virtual_root':vroot, 'virtual_root_path':vroot_tuple, 'root':root} +ModelGraphTraverser = ResourceTreeTraverser # b/w compat + class TraversalContextURL(object): """ The IContextURL adapter used to generate URLs for a context - object obtained via graph traversal""" + object obtained via resource tree traversal""" implements(IContextURL) vroot_varname = VH_ROOT_KEY @@ -631,7 +638,7 @@ class TraversalContextURL(object): environ = self.request.environ vroot_varname = self.vroot_varname if vroot_varname in environ: - return find_model(self.context, environ[vroot_varname]) + return find_resource(self.context, environ[vroot_varname]) # shortcut instead of using find_root; we probably already # have it on the request try: @@ -641,15 +648,15 @@ class TraversalContextURL(object): def __call__(self): """ Generate a URL based on the :term:`lineage` of a - :term:`model` object obtained via :term:`traversal`. If any - model in the context lineage has a Unicode name, it will be + :term:`resource` object obtained via :term:`traversal`. If any + resource in the context lineage has a Unicode name, it will be converted to a UTF-8 string before being attached to the URL. If a ``HTTP_X_VHM_ROOT`` key is present in the WSGI environment, its value will be treated as a 'virtual root path': the path of the URL generated by this will be left-stripped of this virtual root path value. """ - path = model_path(self.context) + path = resource_path(self.context) if path != '/': path = path + '/' request = self.request diff --git a/pyramid/url.py b/pyramid/url.py index 79740821e..b0b686f0c 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -2,6 +2,8 @@ import os +from zope.deprecation import deprecated + from repoze.lru import lru_cache from pyramid.interfaces import IContextURL @@ -73,9 +75,9 @@ def route_url(route_name, request, *elements, **kw): If a keyword argument ``_anchor`` is present, its string representation will be used as a named anchor in the generated URL - (e.g. if ``_anchor`` is passed as ``foo`` and the model URL is - ``http://example.com/model/url``, the resulting generated URL will - be ``http://example.com/model/url#foo``). + (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is + ``http://example.com/route/url``, the resulting generated URL will + be ``http://example.com/route/url#foo``). .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 encoded. If ``_anchor`` is passed as a Unicode object, it @@ -181,42 +183,42 @@ def route_path(route_name, request, *elements, **kw): kw['_app_url'] = '' return route_url(route_name, request, *elements, **kw) -def model_url(model, request, *elements, **kw): +def resource_url(resource, request, *elements, **kw): """ - Generate a string representing the absolute URL of the ``model`` + Generate a string representing the absolute URL of the :term:`resource` object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or ``SERVER_NAME`` in the ``request``, plus any ``SCRIPT_NAME``. The overall result of this function is always a UTF-8 encoded string (never Unicode). - .. note:: Calling :meth:`pyramid.Request.model_url` can be used to - achieve the same result as :func:`pyramid.url.model_url`. + .. note:: Calling :meth:`pyramid.Request.resource_url` can be used to + achieve the same result as :func:`pyramid.url.resource_url`. Examples:: - model_url(context, request) => + resource_url(context, request) => http://example.com/ - model_url(context, request, 'a.html') => + resource_url(context, request, 'a.html') => http://example.com/a.html - model_url(context, request, 'a.html', query={'q':'1'}) => + resource_url(context, request, 'a.html', query={'q':'1'}) => http://example.com/a.html?q=1 - model_url(context, request, 'a.html', anchor='abc') => + resource_url(context, request, 'a.html', anchor='abc') => http://example.com/a.html#abc Any positional arguments passed in as ``elements`` must be strings or Unicode objects. These will be joined by slashes and appended - to the generated model URL. Each of the elements passed in is + to the generated resource URL. Each of the elements passed in is URL-quoted before being appended; if any element is Unicode, it will converted to a UTF-8 bytestring before being URL-quoted. - .. warning:: if no ``elements`` arguments are specified, the model + .. warning:: if no ``elements`` arguments are specified, the resource URL will end with a trailing slash. If any ``elements`` are used, the generated URL will *not* end in trailing a slash. @@ -241,9 +243,9 @@ def model_url(model, request, *elements, **kw): If a keyword argument ``anchor`` is present, its string representation will be used as a named anchor in the generated URL - (e.g. if ``anchor`` is passed as ``foo`` and the model URL is - ``http://example.com/model/url``, the resulting generated URL will - be ``http://example.com/model/url#foo``). + (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is + ``http://example.com/resource/url``, the resulting generated URL will + be ``http://example.com/resource/url#foo``). .. note:: If ``anchor`` is passed as a string, it should be UTF-8 encoded. If ``anchor`` is passed as a Unicode object, it @@ -255,15 +257,15 @@ def model_url(model, request, *elements, **kw): will always follow the query element, e.g. ``http://example.com?foo=1#bar``. - .. note:: If the ``model`` used is the result of a + .. note:: If the :term:`resource` used is the result of a :term:`traversal`, it must be :term:`location`-aware. - The ``model`` can also be the context of a :term:`URL + The resource can also be the context of a :term:`URL dispatch`; contexts found this way do not need to be location-aware. .. note:: If a 'virtual root path' is present in the request environment (the value of the WSGI environ key - ``HTTP_X_VHM_ROOT``), and the ``model`` was obtained via + ``HTTP_X_VHM_ROOT``), and the resource was obtained via :term:`traversal`, the URL path will not include the virtual root prefix (it will be stripped off the left hand side of the generated URL). @@ -273,10 +275,10 @@ def model_url(model, request, *elements, **kw): except AttributeError: reg = get_current_registry() # b/c - context_url = reg.queryMultiAdapter((model, request), IContextURL) + context_url = reg.queryMultiAdapter((resource, request), IContextURL) if context_url is None: - context_url = TraversalContextURL(model, request) - model_url = context_url() + context_url = TraversalContextURL(resource, request) + resource_url = context_url() qs = '' anchor = '' @@ -295,7 +297,14 @@ def model_url(model, request, *elements, **kw): else: suffix = '' - return model_url + suffix + qs + anchor + return resource_url + suffix + qs + anchor + +model_url = resource_url + +deprecated( + 'model_url', + '(The ``pyramid.url.model_url`` API is deprecated as of Pyramid 1.0. Use' + 'the ``pyramid.url.resource_url`` instead.) ') def static_url(path, request, **kw): """ diff --git a/pyramid/zcml.py b/pyramid/zcml.py index db314efa9..8e050e063 100644 --- a/pyramid/zcml.py +++ b/pyramid/zcml.py @@ -402,23 +402,30 @@ def forbidden(_context, wrapper=wrapper) -class IResourceDirective(Interface): +class IAssetDirective(Interface): """ - Directive for specifying that one package may override resources from + Directive for specifying that one package may override assets from another package. """ to_override = TextLine( title=u"Override spec", - description=u'The spec of the resource to override.', + description=u'The spec of the asset to override.', required=True) override_with = TextLine( title=u"With spec", - description=u"The spec of the resource providing the override.", + description=u"The spec of the asset providing the override.", required=True) -def resource(_context, to_override, override_with, _override=None): +def asset(_context, to_override, override_with, _override=None): config = Configurator.with_context(_context) - config.override_resource(to_override, override_with, _override=_override) + config.override_asset(to_override, override_with, _override=_override) + +resource = asset # b/w compat + +deprecated( + 'resource', + '(The ``resource`` ZCML directive is deprecated as of Pyramid 1.0. Use' + 'the ``asset`` directive instead.) ') class IRepozeWho1AuthenticationPolicyDirective(Interface): identifier_name = TextLine(title=u'identitfier_name', required=False, -- cgit v1.2.3