Merge branch 'model2resource'
Conflicts:
docs/narr/views.rst
5 files deleted
6 files added
83 files modified
2 files renamed
| | |
| | | Next release |
| | | ============ |
| | | |
| | | Terminology Changes |
| | | ------------------- |
| | | |
| | | - The Pyramid concept previously known as "model" is now known as "resource". |
| | | As a result: |
| | | |
| | | - The following API changes have been made:: |
| | | |
| | | pyramid.url.model_url -> |
| | | pyramid.url.resource_url |
| | | |
| | | pyramid.traversal.find_model -> |
| | | pyramid.url.find_resource |
| | | |
| | | pyramid.traversal.model_path -> |
| | | pyramid.traversal.resource_path |
| | | |
| | | pyramid.traversal.model_path_tuple -> |
| | | pyramid.traversal.resource_path_tuple |
| | | |
| | | pyramid.traversal.ModelGraphTraverser -> |
| | | pyramid.traversal.ResourceTreeTraverser |
| | | |
| | | pyramid.config.Configurator.testing_models -> |
| | | pyramid.config.Configurator.testing_resources |
| | | |
| | | pyramid.testing.registerModels -> |
| | | pyramid.testing.registerResources |
| | | |
| | | pyramid.testing.DummyModel -> |
| | | pyramid.testing.DummyResource |
| | | |
| | | - All documentation which previously referred to "model" now refers to |
| | | "resource". |
| | | |
| | | - The ``starter`` and ``starter_zcml`` paster templates now have a |
| | | ``resources.py`` module instead of a ``models.py`` module. |
| | | |
| | | - Positional argument names of various APIs have been changed from |
| | | ``model`` to ``resource``. |
| | | |
| | | Backwards compatibility shims have been left in place in all cases. They |
| | | will continue to work "forever". |
| | | |
| | | - The Pyramid concept previously known as "resource" is now known as "asset". |
| | | As a result: |
| | | |
| | | - The (non-API) module previously known as ``pyramid.resource`` is now |
| | | known as ``pyramid.asset``. |
| | | |
| | | - All docs that previously referred to "resource specification" now refer |
| | | to "asset specification". |
| | | |
| | | - The following API changes were made:: |
| | | |
| | | pyramid.config.Configurator.absolute_resource_spec -> |
| | | pyramid.config.Configurator.absolute_asset_spec |
| | | |
| | | pyramid.config.Configurator.override_resource -> |
| | | pyramid.config.Configurator.override_asset |
| | | |
| | | - The ZCML directive previously known as ``resource`` is now known as |
| | | ``asset``. |
| | | |
| | | - The setting previously known as ``BFG_RELOAD_RESOURCES`` (envvar) or |
| | | ``reload_resources`` (config file) is now known, respectively, as |
| | | ``BFG_RELOAD_ASSETS`` and ``reload_assets``. |
| | | |
| | | Backwards compatibility shims have been left in place in all cases. They |
| | | will continue to work "forever". |
| | | |
| | | Bug Fixes |
| | | --------- |
| | | |
| | |
| | | - Direct Jython users to Mako rather than Jinja2 in "Install" narrative |
| | | chapter. |
| | | |
| | | - Many changes to support terminological renaming of "model" to "resource" |
| | | and "resource" to "asset". |
| | | |
| | | Paster Templates |
| | | ---------------- |
| | | |
| | |
| | | |
| | | .. automethod:: maybe_dotted |
| | | |
| | | .. automethod:: absolute_resource_spec |
| | | .. automethod:: absolute_asset_spec |
| | | |
| | | .. automethod:: setup_registry(settings=None, root_factory=None, authentication_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None) |
| | | |
| | |
| | | |
| | | .. automethod:: make_wsgi_app() |
| | | |
| | | .. automethod:: override_resource(to_override, override_with) |
| | | .. automethod:: override_asset(to_override, override_with) |
| | | |
| | | .. automethod:: scan(package=None, categories=None) |
| | | |
| | |
| | | |
| | | .. automethod:: testing_securitypolicy |
| | | |
| | | .. automethod:: testing_models |
| | | .. automethod:: testing_resources |
| | | |
| | | .. automethod:: testing_add_subscriber |
| | | |
| | |
| | | .. attribute:: root |
| | | |
| | | The :term:`root` object will be available as the ``root`` |
| | | attribute of the :term:`request` object. It will be the model |
| | | attribute of the :term:`request` object. It will be the resource |
| | | object at which traversal started (the root). See |
| | | :ref:`traversal_chapter` for information about root objects. |
| | | |
| | |
| | | |
| | | .. autofunction:: registerDummySecurityPolicy |
| | | |
| | | .. autofunction:: registerModels |
| | | .. autofunction:: registerResources |
| | | |
| | | .. autofunction:: registerEventListener |
| | | |
| | |
| | | |
| | | .. autofunction:: cleanUp |
| | | |
| | | .. autoclass:: DummyModel |
| | | .. autoclass:: DummyResource |
| | | :members: |
| | | |
| | | .. autoclass:: DummyRequest |
| | |
| | | |
| | | .. autofunction:: find_interface |
| | | |
| | | .. autofunction:: find_model |
| | | .. autofunction:: find_resource |
| | | |
| | | .. autofunction:: find_root |
| | | |
| | | .. autofunction:: model_path |
| | | .. autofunction:: resource_path |
| | | |
| | | .. autofunction:: model_path_tuple |
| | | .. autofunction:: resource_path_tuple |
| | | |
| | | .. autofunction:: quote_path_segment |
| | | |
| | |
| | | |
| | | .. automodule:: pyramid.url |
| | | |
| | | .. autofunction:: pyramid.url.model_url(context, request, *elements, query=None, anchor=None) |
| | | .. autofunction:: pyramid.url.resource_url(context, request, *elements, query=None, anchor=None) |
| | | |
| | | .. autofunction:: route_url |
| | | |
| | |
| | | Pyramid Uses "Model" To Represent A Node In The Graph of Objects Traversed |
| | | -------------------------------------------------------------------------- |
| | | |
| | | The :app:`Pyramid` documentation refers to the graph being |
| | | traversed when :term:`traversal` is used as a "model graph". Some of |
| | | the :app:`Pyramid` APIs also use the word "model" in them when |
| | | referring to a node in this graph (e.g. ``pyramid.url.model_url``). |
| | | |
| | | A terminology overlap confuses people who write applications that |
| | | always use ORM packages such as SQLAlchemy, which has a different |
| | | notion of the definition of a "model". When using the API of common |
| | | ORM packages, its conception of "model" is almost certainly not a |
| | | directed acyclic graph (as may be the case in many graph databases). |
| | | Often model objects must be explicitly manufactured by an ORM as a |
| | | result of some query performed by a :term:`view`. As a result, it can |
| | | be unnatural to think of the nodes traversed as "model" objects if you |
| | | develop your application using traversal and a relational database. |
| | | When you develop such applications, the things that :app:`Pyramid` |
| | | refers to as "models" in such an application may just be stand-ins |
| | | that perform a query and generate some wrapper *for* an ORM "model" |
| | | (or set of ORM models). The graph *might* be composed completely of |
| | | "model" objects (as defined by the ORM) but it also might not be. |
| | | |
| | | The naming impedance mismatch between the way the term "model" is used |
| | | to refer to a node in a graph in :app:`Pyramid` and the way the |
| | | term "model" is used by packages like SQLAlchemy is unfortunate. For |
| | | the purpose of avoiding confusion, if we had it to do all over again, |
| | | we might refer to the graph that :app:`Pyramid` traverses a "node |
| | | graph" or "object graph" rather than a "model graph", but since we've |
| | | baked the name into the API, it's a little late. Sorry. |
| | | |
| | | In our defense, many :app:`Pyramid` applications (especially ones |
| | | which use :term:`ZODB`) do indeed traverse a graph full of 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. |
| | | The ``repoze.bfg`` documentation used to refer to the graph being traversed |
| | | when :term:`traversal` is used as a "model graph". A terminology overlap |
| | | confused people who wrote applications that always use ORM packages such as |
| | | SQLAlchemy, which has a different notion of the definition of a "model". As |
| | | a sresult, in Pyramid 1.0a7, the tree of objects traversed is now renamed to |
| | | :term:`resource tree` and its components are now named :term:`resource` |
| | | objects. Associated APIs have been changed. This hopefully alleviates the |
| | | terminology confusion caused by overriding the term "model". |
| | | |
| | | Pyramid Does Traversal, And I Don't Like Traversal |
| | | -------------------------------------------------- |
| | | |
| | | In :app:`Pyramid`, :term:`traversal` is the act of resolving a URL |
| | | path to a :term:`model` object in an object graph. Some people are |
| | | uncomfortable with this notion, and believe it is wrong. |
| | | In :app:`Pyramid`, :term:`traversal` is the act of resolving a URL path to a |
| | | :term:`resource` object in a resource tree. Some people are uncomfortable |
| | | with this notion, and believe it is wrong. |
| | | |
| | | This is understandable. The people who believe it is wrong almost |
| | | invariably have all of their data in a relational database. |
| | | Relational databases aren't naturally hierarchical, so "traversing" |
| | | one like a graph is not possible. This problem is related to |
| | | :ref:`model_traversal_confusion`. |
| | | This is understandable. The people who believe it is wrong almost invariably |
| | | have all of their data in a relational database. Relational databases aren't |
| | | naturally hierarchical, so "traversing" one like a tree is not possible. |
| | | |
| | | Folks who deem traversal unilaterally "wrong" are neglecting to take |
| | | into account that many persistence mechanisms *are* hierarchical. |
| | | Examples include a filesystem, an LDAP database, a :term:`ZODB` (or |
| | | another type of graph) database, an XML document, and the Python |
| | | module namespace. It is often convenient to model the frontend to a |
| | | hierarchical data store as a graph, using traversal to apply views to |
| | | objects that either *are* the nodes in the graph being traversed (such |
| | | as in the case of ZODB) or at least ones which stand in for them (such |
| | | as in the case of wrappers for files from the filesystem). |
| | | Folks who deem traversal unilaterally "wrong" are neglecting to take into |
| | | account that many persistence mechanisms *are* hierarchical. Examples |
| | | include a filesystem, an LDAP database, a :term:`ZODB` (or another type of |
| | | graph) database, an XML document, and the Python module namespace. It is |
| | | often convenient to model the frontend to a hierarchical data store as a |
| | | graph, using traversal to apply views to objects that either *are* the |
| | | resources in the tree being traversed (such as in the case of ZODB) or at |
| | | least ones which stand in for them (such as in the case of wrappers for files |
| | | from the filesystem). |
| | | |
| | | Also, many website structures are naturally hierarchical, even if the |
| | | data which drives them isn't. For example, newspaper websites are |
| | | often extremely hierarchical: sections within sections within |
| | | sections, ad infinitum. If you want your URLs to indicate this |
| | | structure, and the structure is indefinite (the number of nested |
| | | sections can be "N" instead of some fixed number), traversal is an |
| | | excellent way to model this, even if the backend is a relational |
| | | database. In this situation, the graph being traversed is actually |
| | | less a "model graph" than a site structure. |
| | | Also, many website structures are naturally hierarchical, even if the data |
| | | which drives them isn't. For example, newspaper websites are often extremely |
| | | hierarchical: sections within sections within sections, ad infinitum. If you |
| | | want your URLs to indicate this structure, and the structure is indefinite |
| | | (the number of nested sections can be "N" instead of some fixed number), a |
| | | resource tree is an excellent way to model this, even if the backend is a |
| | | relational database. In this situation, the resource tree a just a site |
| | | structure. |
| | | |
| | | But the point is ultimately moot. If you use :app:`Pyramid`, and |
| | | you don't want to model your application in terms of traversal, you |
| | | needn't use it at all. Instead, use :term:`URL dispatch` to map URL |
| | | paths to views. |
| | | But the point is ultimately moot. If you use :app:`Pyramid`, and you don't |
| | | want to model your application in terms of a resource tree, you needn't use |
| | | it at all. Instead, use :term:`URL dispatch` to map URL paths to views. |
| | | |
| | | Pyramid Does URL Dispatch, And I Don't Like URL Dispatch |
| | | -------------------------------------------------------- |
| | |
| | | a route like ``<route name="manage" pattern="manage/*traverse"/>`` and then |
| | | associate "management" views in your code by using the ``route_name`` |
| | | argument to a ``view`` configuration, e.g. ``<view view=".some.callable" |
| | | context=".some.Model" route_name="manage"/>``. If you wire things up this |
| | | context=".some.Resource" route_name="manage"/>``. If you wire things up this |
| | | way someone then walks up to for example, ``/manage/ob1/ob2``, they might be |
| | | presented with a management interface, but walking up to ``/ob1/ob2`` would |
| | | present them with the default object view. There are other tricks you can |
| | |
| | | return HttpResponse(poll_id) |
| | | |
| | | Zope, likewise allows you to add arbitrary keyword and positional |
| | | arguments to any method of a model object found via traversal: |
| | | arguments to any method of a resource object found via traversal: |
| | | |
| | | .. ignore-next-block |
| | | .. code-block:: python |
| | |
| | | detect when the model changes. The web just has no such facility in |
| | | its current form: it's effectively pull-only. |
| | | |
| | | So, in the interest of not mistaking desire with reality, and instead |
| | | of trying to jam the square peg that is the web into the round hole of |
| | | "MVC", we just punt and say there are two things: the model, and the |
| | | view. The model stores the data, the view presents it. The templates |
| | | are really just an implementation detail of any given view: a view |
| | | doesn't need a template to return a response. There's no |
| | | "controller": it just doesn't exist. This seems to us like a more |
| | | reasonable model, given the current constraints of the web. |
| | | So, in the interest of not mistaking desire with reality, and instead of |
| | | trying to jam the square peg that is the web into the round hole of "MVC", we |
| | | just punt and say there are two things: resources and views. The resource |
| | | tree represents a site structure, the view presents a resource. The |
| | | templates are really just an implementation detail of any given view: a view |
| | | doesn't need a template to return a response. There's no "controller": it |
| | | just doesn't exist. The "model" is either represented by the resource tree |
| | | or by a "domain model" (like a SQLAlchemy model) that is separate from the |
| | | framework entirely. This seems to us like more reasonable terminology, given |
| | | the current constraints of the web. |
| | | |
| | | .. _apps_are_extensible: |
| | | |
| | |
| | | permissions that the accessing user possesses with respect to a context |
| | | object. |
| | | |
| | | #) I want to also expose my model via a REST API using Twisted Web. If |
| | | #) I want to also expose my resources via a REST API using Twisted Web. If |
| | | Pyramid performed authorization based on attribute access via Zope3's |
| | | security proies, I could enforce my authorization policy in both |
| | | security proxies, I could enforce my authorization policy in both |
| | | :app:`Pyramid` and in the Twisted-based system the same way. |
| | | |
| | | Defense |
| | |
| | | |
| | | pkg_resources |
| | | A module which ships with :term:`setuptools` that provides an API |
| | | for addressing "resource files" within Python packages. Resource |
| | | for addressing "asset files" within Python packages. Asset |
| | | files are static files, template files, etc; basically anything |
| | | non-Python-source that lives in a Python package can be considered |
| | | a resource file. See also `PkgResources |
| | | a asset file. See also `PkgResources |
| | | <http://peak.telecommunity.com/DevCenter/PkgResources>`_ |
| | | |
| | | resource |
| | | asset |
| | | Any file contained within a Python :term:`package` which is *not* |
| | | a Python source code file. |
| | | |
| | | resource specification |
| | | A colon-delimited identifier for a :term:`resource`. The colon |
| | | asset specification |
| | | A colon-delimited identifier for an :term:`asset`. The colon |
| | | separates a Python :term:`package` name from a package subpath. |
| | | For example, the resource specification |
| | | 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`. |
| | |
| | | string (which implies the :term:`default view`). |
| | | |
| | | Default view |
| | | The default view of a model is the view invoked when the |
| | | :term:`view name` is the empty string (``''``). This is the case |
| | | when :term:`traversal` exhausts the path elements in the PATH_INFO |
| | | of a request before it returns a :term:`context`. |
| | | The default view of a :term:`resource` is the view invoked when the |
| | | :term:`view name` is the empty string (``''``). This is the case when |
| | | :term:`traversal` exhausts the path elements in the PATH_INFO of a |
| | | request before it returns a :term:`context`. |
| | | |
| | | virtualenv |
| | | An isolated Python environment. Allows you to control which |
| | |
| | | Python. `virtualenv <http://pypi.python.org/pypi/virtualenv>`_ |
| | | was created by Ian Bicking. |
| | | |
| | | model |
| | | An object representing data in the system. If :mod:`traversal` is |
| | | used, a model is a node in the object graph traversed by the |
| | | system. When traversal is used, a model instance becomes the |
| | | :term:`context` of a :term:`view`. If :mod:`url dispatch` is |
| | | used, a single :term:`context` is generated for each request and |
| | | is used as the context of a view: this object is also technically |
| | | a "model" in :app:`Pyramid` terms, although this terminology |
| | | can be a bit confusing: see :ref:`model_traversal_confusion`. |
| | | resource |
| | | An object representing a node in the :term:`resource tree` of an |
| | | application. If :mod:`traversal` is used, a resource is an element in |
| | | the resource tree traversed by the system. When traversal is used, a |
| | | resource becomes the :term:`context` of a :term:`view`. If :mod:`url |
| | | dispatch` is used, a single resource is generated for each request and |
| | | is used as the context of a view. |
| | | |
| | | resource tree |
| | | A nested set of dictionary-like objects, each of which is a |
| | | :term:`resource`. The act of :term:`traversal` uses the resource tree |
| | | to find a :term:`context`. |
| | | |
| | | domain model |
| | | Persistent data related to your application. For example, data stored |
| | | in a relational database. In some applications, the :term:`resource |
| | | tree` acts as the domain model. |
| | | |
| | | traversal |
| | | The act of descending "down" a graph of model objects from a root |
| | | model in order to find a :term:`context`. The :app:`Pyramid` |
| | | :term:`router` performs traversal of model objects when a |
| | | :term:`root factory` is specified. See the |
| | | :ref:`traversal_chapter` chapter for more information. Traversal |
| | | can be performed *instead* of :term:`URL dispatch` or can be |
| | | combined *with* URL dispatch. See :ref:`hybrid_chapter` for more |
| | | information about combining traversal and URL dispatch (advanced). |
| | | The act of descending "up" a tree of resource objects from a root |
| | | resource in order to find a :term:`context`. The :app:`Pyramid` |
| | | :term:`router` performs traversal of resource objects when a :term:`root |
| | | factory` is specified. See the :ref:`traversal_chapter` chapter for |
| | | more information. Traversal can be performed *instead* of :term:`URL |
| | | dispatch` or can be combined *with* URL dispatch. See |
| | | :ref:`hybrid_chapter` for more information about combining traversal and |
| | | URL dispatch (advanced). |
| | | |
| | | router |
| | | The :term:`WSGI` application created when you start a |
| | |
| | | :app:`Pyramid` application. |
| | | |
| | | URL dispatch |
| | | An alternative to graph traversal as a mechanism for locating a |
| | | An alternative to :term:`traversal` as a mechanism for locating a |
| | | :term:`context` for a :term:`view`. When you use a :term:`route` |
| | | in your :app:`Pyramid` application via a :term:`route |
| | | configuration`, you are using URL dispatch. See the |
| | |
| | | |
| | | context |
| | | An object in the system that is found during :term:`traversal` or |
| | | :term:`URL dispatch` based on URL data; if it's found via |
| | | traversal, it's usually a :term:`model` object that is part of an |
| | | object graph; if it's found via :term:`URL dispatch`, it's a |
| | | object manufactured on behalf of the route's "factory". A context |
| | | becomes the subject of a :term:`view`, and typically has security |
| | | information attached to it. See the :ref:`traversal_chapter` |
| | | chapter and the :ref:`urldispatch_chapter` chapter for more |
| | | information about how a URL is resolved to a context. |
| | | :term:`URL dispatch` based on URL data; if it's found via traversal, |
| | | it's usually a :term:`resource` object that is part of a resource tree; |
| | | if it's found via :term:`URL dispatch`, it's a object manufactured on |
| | | behalf of the route's "factory". A context becomes the subject of a |
| | | :term:`view`, and typically has security information attached to it. |
| | | See the :ref:`traversal_chapter` chapter and the |
| | | :ref:`urldispatch_chapter` chapter for more information about how a URL |
| | | is resolved to a context. |
| | | |
| | | application registry |
| | | A registry of configuration information consulted by |
| | | :app:`Pyramid` while servicing an application. An application |
| | | registry maps model types to views, as well as housing other |
| | | registry maps resource types to views, as well as housing other |
| | | application-specific component registrations. Every |
| | | :app:`Pyramid` application has one (and only one) application |
| | | registry. |
| | |
| | | text, XML, or HTML when rendered. |
| | | |
| | | location |
| | | The path to an object in an object graph. See :ref:`location_aware` |
| | | for more information about how to make a model object *location-aware*. |
| | | The path to an object in a :term:`resource tree`. See |
| | | :ref:`location_aware` for more information about how to make a resource |
| | | object *location-aware*. |
| | | |
| | | permission |
| | | A string or unicode object that represents an action being taken |
| | | against a context. A permission is associated with a view name |
| | | and a model type by the developer. Models are decorated with |
| | | security declarations (e.g. an :term:`ACL`), which reference these |
| | | tokens also. Permissions are used by the active to security |
| | | policy to match the view permission against the model's statements |
| | | about which permissions are granted to which principal in a |
| | | context in order to to answer the question "is this user allowed |
| | | to do this". Examples of permissions: ``read``, or |
| | | ``view_blog_entries``. |
| | | A string or unicode object that represents an action being taken against |
| | | a context. A permission is associated with a view name and a resource |
| | | type by the developer. Resources are decorated with security |
| | | declarations (e.g. an :term:`ACL`), which reference these tokens also. |
| | | Permissions are used by the active to security policy to match the view |
| | | permission against the resources's statements about which permissions |
| | | are granted to which principal in a context in order to to answer the |
| | | question "is this user allowed to do this". Examples of permissions: |
| | | ``read``, or ``view_blog_entries``. |
| | | |
| | | default permission |
| | | A :term:`permission` which is registered as the default for an |
| | |
| | | |
| | | ACL |
| | | An *access control list*. An ACL is a sequence of :term:`ACE` |
| | | tuples. An ACL is attached to a model instance. An example of an |
| | | tuples. An ACL is attached to a resource instance. An example of an |
| | | ACL is ``[ (Allow, 'bob', 'read'), (Deny, 'fred', 'write')]``. If |
| | | an ACL is attached to a model instance, and that model instance is |
| | | an ACL is attached to a resource instance, and that resource is |
| | | findable via the context, it will be consulted any active security |
| | | policy to determine wither a particular request can be fulfilled |
| | | given the :term:`authentication` information in the request. |
| | |
| | | interface |
| | | A `Zope interface <http://pypi.python.org/pypi/zope.interface>`_ |
| | | object. In :app:`Pyramid`, an interface may be attached to a |
| | | :term:`model` object or a :term:`request` object in order to |
| | | :term:`resource` object or a :term:`request` object in order to |
| | | identify that the object is "of a type". Interfaces are used |
| | | internally by :app:`Pyramid` to perform view lookups and other |
| | | policy lookups. The ability to make use of an interface is |
| | |
| | | :app:`Pyramid` to form a workflow system. |
| | | |
| | | virtual root |
| | | A model object representing the "virtual" root of a request; this |
| | | A resource object representing the "virtual" root of a request; this |
| | | is typically the physical root object (the object returned by the |
| | | application root factory) unless :ref:`vhosting_chapter` is in |
| | | use. |
| | |
| | | narr/webob |
| | | narr/sessions |
| | | narr/templates |
| | | narr/models |
| | | narr/resources |
| | | narr/security |
| | | narr/i18n |
| | | narr/vhosting |
| | |
| | | narr/hooks |
| | | narr/declarative |
| | | narr/extending |
| | | narr/resources |
| | | narr/assets |
| | | narr/router |
| | | narr/threadlocals |
| | | narr/zca |
| | |
| | | narr/webob |
| | | narr/sessions |
| | | narr/templates |
| | | narr/models |
| | | narr/resources |
| | | narr/security |
| | | narr/i18n |
| | | narr/vhosting |
| | |
| | | narr/hooks |
| | | narr/declarative |
| | | narr/extending |
| | | narr/resources |
| | | narr/assets |
| | | narr/router |
| | | narr/startup |
| | | narr/threadlocals |
| | |
| | | zcml/aclauthorizationpolicy |
| | | zcml/adapter |
| | | zcml/authtktauthenticationpolicy |
| | | zcml/asset |
| | | zcml/configure |
| | | zcml/default_permission |
| | | zcml/forbidden |
| | |
| | | zcml/remoteuserauthenticationpolicy |
| | | zcml/renderer |
| | | zcml/repozewho1authenticationpolicy |
| | | zcml/resource |
| | | zcml/route |
| | | zcml/scan |
| | | zcml/static |
| | |
| | | from pyramid.config import Configurator |
| | | from myproject.models import get_root |
| | | from myproject.resources import Root |
| | | |
| | | def main(global_config, **settings): |
| | | """ This function returns a Pyramid WSGI application. |
| | | """ |
| | | config = Configurator(root_factory=get_root, settings=settings) |
| | | config = Configurator(root_factory=Root, settings=settings) |
| | | config.add_view('myproject.views.my_view', |
| | | context='myproject.models.MyModel', |
| | | context='myproject.resources.Root', |
| | | renderer='myproject:templates/mytemplate.pt') |
| | | config.add_static_view('static', 'myproject:static') |
| | | return config.make_wsgi_app() |
New file |
| | |
| | | class Root(object): |
| | | def __init__(self, request): |
| | | self.request = request |
New file |
| | |
| | | .. 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. |
| | | |
| | |
| | | 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. |
| | | |
| | |
| | | :linenos: |
| | | |
| | | <view |
| | | context=".models.Hello" |
| | | context=".resources.Hello" |
| | | view=".views.hello_world" |
| | | name="hello.html" |
| | | /> |
| | |
| | | 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``. |
| | | |
| | |
| | | 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: |
| | | |
| | | <view |
| | | context=".models.Hello" |
| | | context=".resources.Hello" |
| | | view=".views.hello_world" |
| | | /> |
| | | |
| | | 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``. |
| | |
| | | :linenos: |
| | | |
| | | <view |
| | | context=".models.Hello" |
| | | context=".resources.Hello" |
| | | view=".views.hello_world" |
| | | name="" |
| | | /> |
| | |
| | | .. index:: |
| | | triple: view; zcml; static resource |
| | | |
| | | .. _zcml_static_resources_section: |
| | | .. _zcml_static_assets_section: |
| | | |
| | | Serving Static Resources Using ZCML |
| | | ------------------------------------ |
| | | Serving Static Assets Using ZCML |
| | | -------------------------------- |
| | | |
| | | Use of the ``static`` ZCML directive makes static files available at a name |
| | | relative to the application root URL, e.g. ``/static``. |
| | | Use of the ``static`` ZCML directive makes static assets available at a name |
| | | relative to the application root URL, e.g. ``/static``. |
| | | |
| | | Note that the ``path`` provided to the ``static`` ZCML directive may be a |
| | | fully qualified :term:`resource specification`, a package-relative path, or |
| | | fully qualified :term:`asset specification`, a package-relative path, or |
| | | an *absolute path*. The ``path`` with the value ``a/b/c/static`` of a |
| | | ``static`` directive in a ZCML file that resides in the "mypackage" package |
| | | will resolve to a package-qualified resource such as |
| | | will resolve to a package-qualified assets such as |
| | | ``some_package:a/b/c/static``. |
| | | |
| | | Here's an example of a ``static`` ZCML directive that will serve files |
| | |
| | | Here's an example of a ``static`` directive 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`. |
| | | :term:`asset specification`. |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | |
| | | path="static" |
| | | /> |
| | | |
| | | Whether you use for ``path`` a fully qualified resource specification, |
| | | Whether you use for ``path`` a fully qualified asset specification, |
| | | an absolute path, or a package-relative 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 |
| | |
| | | ``name`` argument of the ``static`` ZCML directive 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 |
| | | 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`. |
| | |
| | | The :meth:`pyramid.config.Configurator.add_static_view` method offers |
| | | an imperative equivalent to the ``static`` ZCML directive. Use of the |
| | | ``add_static_view`` imperative configuration method is completely equivalent |
| | | to using ZCML for the same purpose. See :ref:`static_resources_section` for |
| | | to using ZCML for the same purpose. See :ref:`static_assets_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: |
| | | |
| | | <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`` |
| | | 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: |
| | | |
| | | <asset |
| | | to_override="some.package" |
| | | override_with="another.package" |
| | | /> |
| | | |
| | | Individual subdirectories within a package can also be overridden: |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | |
| | | <asset |
| | | to_override="some.package:templates/" |
| | | override_with="another.package:othertemplates/" |
| | | /> |
| | | |
| | | 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: |
| | | |
| | | <asset |
| | | to_override=".subpackage:templates/" |
| | | override_with="another.package:templates/" |
| | | /> |
| | | |
| | | See also :ref:`asset_directive`. |
| | | |
| | | .. _zcml_authorization_policy: |
| | | |
| | |
| | | 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: |
| | |
| | | ``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: |
| | | |
| | |
| | | |
| | | See also :ref:`renderer_directive` and |
| | | :meth:`pyramid.config.Configurator.add_renderer`. |
| | | |
| | | |
| | | Overriding an Existing Renderer |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | |
| | | |
| | | .. - hooks chapter still has topics for ZCML |
| | | |
| | | .. - resources chapter still has topics for ZCML |
| | |
| | | single: settings |
| | | single: reload |
| | | single: debug_authorization |
| | | single: reload_resources |
| | | single: reload_assets |
| | | single: debug_notfound |
| | | single: debug_all |
| | | single: reload_all |
| | |
| | | | | | |
| | | +---------------------------------+-----------------------------+ |
| | | |
| | | Reloading Resources |
| | | ------------------- |
| | | Reloading Assets |
| | | ---------------- |
| | | |
| | | Don't cache any resource file data when this value is true. See |
| | | also :ref:`overriding_resources_section`. |
| | | Don't cache any asset file data when this value is true. See |
| | | also :ref:`overriding_assets_section`. |
| | | |
| | | +---------------------------------+-----------------------------+ |
| | | | Environment Variable Name | Config File Setting Name | |
| | | +=================================+=============================+ |
| | | | ``BFG_RELOAD_RESOURCES`` | ``reload_resources`` | |
| | | | ``BFG_RELOAD_ASSETS`` | ``reload_assets`` | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | +---------------------------------+-----------------------------+ |
| | | |
| | | .. note:: For backwards compatibility purposes, the following aliases can be |
| | | used for configurating asset reloading: ``BFG_RELOAD_RESOURCES`` (envvar) |
| | | and ``reload_resources`` (config file). |
| | | |
| | | Debugging Authorization |
| | | ----------------------- |
| | |
| | | ++++++++++++++++ |
| | | |
| | | The value(s) supplied here are passed in as the template directories. They |
| | | should be in :term:`resource specification` format, for example: |
| | | should be in :term:`asset specification` format, for example: |
| | | ``my.package:templates``. |
| | | |
| | | +-----------------------------+ |
| | |
| | | |
| | | .. index:: |
| | | single: reload_templates |
| | | single: reload_resources |
| | | single: reload_assets |
| | | |
| | | Understanding the Distinction Between ``reload_templates`` and ``reload_resources`` |
| | | ----------------------------------------------------------------------------------- |
| | | Understanding the Distinction Between ``reload_templates`` and ``reload_assets`` |
| | | -------------------------------------------------------------------------------- |
| | | |
| | | The difference between ``reload_resources`` and ``reload_templates`` |
| | | is a bit subtle. Templates are themselves also treated by |
| | | :app:`Pyramid` as :term:`pkg_resources` resource files (along with |
| | | static files and other resources), so the distinction can be |
| | | confusing. It's helpful to read :ref:`overriding_resources_section` |
| | | for some context about resources in general. |
| | | The difference between ``reload_assets`` and ``reload_templates`` is a bit |
| | | subtle. Templates are themselves also treated by :app:`Pyramid` as asset |
| | | files (along with other static files), so the distinction can be confusing. |
| | | It's helpful to read :ref:`overriding_assets_section` for some context |
| | | about assets in general. |
| | | |
| | | When ``reload_templates`` is true, :app:`Pyramid` takes advantage |
| | | of the underlying templating systems' ability to check for file |
| | | modifications to an individual template file. When |
| | | ``reload_templates`` is true but ``reload_resources`` is *not* true, |
| | | the template filename returned by pkg_resources is cached by |
| | | :app:`Pyramid` on the first request. Subsequent requests for the |
| | | same template file will return a cached template filename. The |
| | | underlying templating system checks for modifications to this |
| | | particular file for every request. Setting ``reload_templates`` to |
| | | ``True`` doesn't affect performance dramatically (although it should |
| | | still not be used in production because it has some effect). |
| | | When ``reload_templates`` is true, :app:`Pyramid` takes advantage of the |
| | | underlying templating systems' ability to check for file modifications to an |
| | | individual template file. When ``reload_templates`` is true but |
| | | ``reload_assets`` is *not* true, the template filename returned by the |
| | | ``pkg_resources`` package (used under the hood by asset resolution) is cached |
| | | by :app:`Pyramid` on the first request. Subsequent requests for the same |
| | | template file will return a cached template filename. The underlying |
| | | templating system checks for modifications to this particular file for every |
| | | request. Setting ``reload_templates`` to ``True`` doesn't affect performance |
| | | dramatically (although it should still not be used in production because it |
| | | has some effect). |
| | | |
| | | However, when ``reload_resources`` is true, :app:`Pyramid` will not |
| | | cache the template filename, meaning you can see the effect of |
| | | changing the content of an overridden resource directory for templates |
| | | without restarting the server after every change. Subsequent requests |
| | | for the same template file may return different filenames based on the |
| | | current state of overridden resource directories. Setting |
| | | ``reload_resources`` to ``True`` affects performance *dramatically*, |
| | | slowing things down by an order of magnitude for each template |
| | | rendering. However, it's convenient to enable when moving files |
| | | around in overridden resource directories. ``reload_resources`` makes |
| | | the system *very slow* when templates are in use. Never set |
| | | ``reload_resources`` to ``True`` on a production system. |
| | | However, when ``reload_assets`` is true, :app:`Pyramid` will not cache the |
| | | template filename, meaning you can see the effect of changing the content of |
| | | an overridden asset directory for templates without restarting the server |
| | | after every change. Subsequent requests for the same template file may |
| | | return different filenames based on the current state of overridden asset |
| | | directories. Setting ``reload_assets`` to ``True`` affects performance |
| | | *dramatically*, slowing things down by an order of magnitude for each |
| | | template rendering. However, it's convenient to enable when moving files |
| | | around in overridden asset directories. ``reload_assets`` makes the system |
| | | *very slow* when templates are in use. Never set ``reload_assets`` to |
| | | ``True`` on a production system. |
| | | |
| | |
| | | :linenos: |
| | | |
| | | <view |
| | | context="theoriginalapplication.models.SomeModel" |
| | | context="theoriginalapplication.resources.SomeResource" |
| | | name="theview" |
| | | view=".views.a_view_that_does_something_slightly_different" |
| | | /> |
| | | |
| | | A similar pattern can be used to *extend* the application with |
| | | ``<view>`` 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 ``<view>`` |
| | | 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 |
| | |
| | | When calling :meth:`~pyramid.config.Configurator.add_handler`, an |
| | | ``action`` is required in either the route pattern or as a keyword argument, |
| | | but **cannot appear in both places**. A ``handler`` argument must also be |
| | | supplied, which can be either a :term:`resource specification` or a Python |
| | | supplied, which can be either a :term:`asset specification` or a Python |
| | | reference to the handler class. Additional keyword arguments are passed |
| | | directly through to :meth:`pyramid.config.Configurator.add_route`. |
| | | |
| | |
| | | config.add_handler('hello', '/hello/{action}', |
| | | handler='mypackage.handlers:MyHandler') |
| | | |
| | | In larger applications, it is advised to use a :term:`resource specification` |
| | | In larger applications, it is advised to use a :term:`asset specification` |
| | | with :meth:`~pyramid.config.Configurator.add_handler` to avoid having |
| | | to import every handler class. |
| | | |
| | |
| | | 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 |
| | |
| | | """ 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 |
| | |
| | | <adapter |
| | | factory="myapp.traversal.Traverser" |
| | | provides="pyramid.interfaces.ITraverser" |
| | | for="myapp.models.MyRoot" |
| | | for="myapp.resources.MyRoot" |
| | | /> |
| | | |
| | | 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``: |
| | | |
| | |
| | | <adapter |
| | | factory="myapp.traversal.URLGenerator" |
| | | provides="pyramid.interfaces.IContextURL" |
| | | for="myapp.models.MyRoot *" |
| | | for="myapp.resources.MyRoot *" |
| | | /> |
| | | |
| | | 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). |
| | | |
| | |
| | | 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: |
| | | |
| | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | class Traversable(object): |
| | | class Resource(object): |
| | | def __init__(self, subobjects): |
| | | self.subobjects = subobjects |
| | | |
| | |
| | | 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: |
| | |
| | | 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:: |
| | | |
| | |
| | | |
| | | 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 |
| | |
| | | :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`, |
| | |
| | | - 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. |
| | |
| | | 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 |
| | |
| | | |
| | | .. 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 |
| | | resource tree, which represents the site structure, and views, which tend |
| | | to present the data stored in the resource tree and a user-defined "domain |
| | | model". However, no facility provided *by the framework* actually |
| | | necessarily maps to the concept of a "controller" or "model". So if you |
| | | had to give it some acronym, I guess you'd say :app:`Pyramid` is actually |
| | | an "RV" framework rather than an "MVC" framework. "MVC", however, is |
| | | close enough as a general classification moniker for purposes of |
| | | comparison with other web frameworks. |
| | |
| | | |
| | | 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`` |
| | |
| | | Type "help" for more information. "root" is the Pyramid app root object, |
| | | "registry" is the Pyramid registry object. |
| | | >>> root |
| | | <myproject.models.MyModel object at 0x445270> |
| | | <myproject.resources.MyResource object at 0x445270> |
| | | >>> registry |
| | | <Registry myproject> |
| | | >>> registry.settings['debug_notfound'] |
| | |
| | | |-- development.ini |
| | | |-- myproject |
| | | | |-- __init__.py |
| | | | |-- models.py |
| | | | |-- resources.py |
| | | | |-- static |
| | | | | |-- favicon.ico |
| | | | | |-- logo.png |
| | |
| | | application, include a ``main`` function which is used as a Paste entry |
| | | point. |
| | | |
| | | #. A ``models.py`` module, which contains :term:`model` code. |
| | | #. A ``resources.py`` module, which contains :term:`resource` code. |
| | | |
| | | #. A ``templates`` directory, which contains :term:`Chameleon` (or |
| | | other types of) templates. |
| | |
| | | #. Line 1 imports the :term:`Configurator` class from |
| | | :mod:`pyramid.config` that we use later. |
| | | |
| | | #. Line 2 imports the ``get_root`` function from |
| | | :mod:`myproject.models` that we use later. |
| | | #. Line 2 imports the ``Root`` class from :mod:`myproject.resources` that we |
| | | use later. |
| | | |
| | | #. Lines 4-12 define a function that returns a :app:`Pyramid` |
| | | WSGI application. This function is meant to be called |
| | |
| | | Lines 8-10 register a "default view" (a view that has no ``name`` |
| | | attribute). It is registered so that it will be found when the |
| | | :term:`context` of the request is an instance of the |
| | | :class:`myproject.models.MyModel` class. The first argument to |
| | | :class:`myproject.resources.Root` class. The first argument to |
| | | ``add_view`` points at a Python function that does all the work for this |
| | | view, also known as a :term:`view callable`, via a :term:`dotted Python |
| | | name`. The view declaration also names a ``renderer``, which in this case |
| | | is a template that will be used to render the result of the view callable. |
| | | This particular view declaration points at |
| | | ``myproject:templates/mytemplate.pt``, which is a :term:`resource |
| | | ``myproject:templates/mytemplate.pt``, which is a :term:`asset |
| | | specification` that specifies the ``mytemplate.pt`` file within the |
| | | ``templates`` directory of the ``myproject`` package. The template file |
| | | it actually points to is a :term:`Chameleon` ZPT template file. |
| | | |
| | | Line 11 registers a static view, which will serve up the files from the |
| | | ``mypackage:static`` :term:`resource specification` (the ``static`` |
| | | ``mypackage:static`` :term:`asset specification` (the ``static`` |
| | | directory of the ``mypackage`` package). |
| | | |
| | | Line 12 returns a :term:`WSGI` application to the caller of the function |
| | |
| | | |
| | | This bit of code was registered as the view callable within ``__init__.py`` |
| | | (via ``add_view``). ``add_view`` said that the default URL for instances |
| | | that are of the class :class:`myproject.models.MyModel` should run this |
| | | that are of the class :class:`myproject.resources.Root` should run this |
| | | :func:`myproject.views.my_view` function. |
| | | |
| | | This view callable function is handed a single piece of information: |
| | |
| | | the speed at which templates may be rendered. |
| | | |
| | | .. index:: |
| | | single: models.py |
| | | single: resources.py |
| | | |
| | | .. _modelspy_project_section: |
| | | .. _resourcespy_project_section: |
| | | |
| | | ``models.py`` |
| | | ~~~~~~~~~~~~~ |
| | | ``resources.py`` |
| | | ~~~~~~~~~~~~~~~~ |
| | | |
| | | The ``models.py`` module provides the :term:`model` data and behavior |
| | | for our application. Models are objects which store application data |
| | | and provide APIs which mutate and return this data. We write a class |
| | | named ``MyModel`` that provides the behavior. |
| | | The ``resources.py`` module provides the :term:`resource` data and behavior |
| | | for our application. Resources are objects which exist to provide site |
| | | structure in applications which use :term:`traversal` to map URLs to code. |
| | | We write a class named ``Root`` that provides the behavior for the root |
| | | resource. |
| | | |
| | | .. literalinclude:: MyProject/myproject/models.py |
| | | .. literalinclude:: MyProject/myproject/resources.py |
| | | :language: python |
| | | :linenos: |
| | | |
| | | #. Lines 1-2 define the MyModel class. |
| | | #. Lines 1-3 define the Root class. The Root class is a "root resource |
| | | factory" function that will be called by the :app:`Pyramid` *Router* for |
| | | each request when it wants to find the root of the resource tree. |
| | | |
| | | #. Line 4 defines an instance of MyModel as the root. |
| | | |
| | | #. 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 |
| | | ``get_root``. |
| | | |
| | | In a "real" application, the root object would not be such a simple |
| | | object. Instead, it would be an object that could access some |
| | | persistent data store, such as a database. :app:`Pyramid` doesn't |
| | | make any assumption about which sort of datastore you'll want to use, |
| | | so the sample application uses an instance of |
| | | :class:`myproject.models.MyModel` to represent the root. |
| | | In a "real" application, the Root object would not be such a simple object. |
| | | Instead, it might be an object that could access some persistent data store, |
| | | such as a database. :app:`Pyramid` doesn't make any assumption about which |
| | | sort of datastore you'll want to use, so the sample application uses an |
| | | instance of :class:`myproject.resources.Root` to represent the root. |
| | | |
| | | ``static`` |
| | | ~~~~~~~~~~~~~~~~~~~~ |
| | | ~~~~~~~~~~ |
| | | |
| | | This directory contains static resources which support the |
| | | ``mytemplate.pt`` template. It includes CSS and images. |
| | | This directory contains static assets which support the ``mytemplate.pt`` |
| | | template. It includes CSS and images. |
| | | |
| | | .. index:: |
| | | single: tests.py |
| | |
| | | .. 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:`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. |
| | | |
| | | - a :term:`Chameleon` template file contained within a Python package. |
| | | 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`. |
| | | |
| | | - a GIF image file contained within a Python package. |
| | | 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. |
| | | |
| | | - a CSS file contained within a Python package. |
| | | 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:`domain model` of the |
| | | application. |
| | | |
| | | - a JavaScript source file contained within a Python package. |
| | | Also: |
| | | |
| | | - 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 ``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`. |
| | | |
| | | 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. |
| | | - A :term:`root factory` returns a resource. |
| | | |
| | | .. _understanding_resources: |
| | | |
| | | Understanding Resources |
| | | ----------------------- |
| | | |
| | | 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: |
| | | |
| | | .. 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 *resource name* (``templates/some_template.pt``) |
| | | |
| | | :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 is exposed to :term:`view` code as the :term:`context` of a |
| | | view. |
| | | |
| | | .. index:: |
| | | pair: overriding; resources |
| | | single: resource constructor |
| | | |
| | | .. _overriding_resources_section: |
| | | Defining a Resource Constructor |
| | | ------------------------------- |
| | | |
| | | Overriding Resources |
| | | -------------------- |
| | | An example of a resource constructor, ``BlogEntry`` is presented below. It |
| | | is implemented as a class which, when instantiated, becomes a resource |
| | | instance. |
| | | |
| | | 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. |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | 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: |
| | | import datetime |
| | | |
| | | - Individual :term:`Chameleon` templates. |
| | | class BlogEntry(object): |
| | | def __init__(self, title, body, author): |
| | | self.title = title |
| | | self.body = body |
| | | self.author = author |
| | | self.created = datetime.datetime.now() |
| | | |
| | | - 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 resource (or set of resources) addressed by code that uses |
| | | the setuptools :term:`pkg_resources` API. |
| | | 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:: |
| | | single: override_resource |
| | | single: resource interfaces |
| | | |
| | | .. _override_resource: |
| | | .. _resources_which_implement_interfaces: |
| | | |
| | | The ``override_resource`` API |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | Resources Which Implement Interfaces |
| | | ------------------------------------ |
| | | |
| | | An individual call to |
| | | :meth:`pyramid.config.Configurator.override_resource` can |
| | | override a single resource. For example: |
| | | 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`. |
| | | |
| | | .. ignore-next-block |
| | | 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. |
| | | |
| | | 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: |
| | | |
| | | config.override_resource( |
| | | to_override='some.package:templates/mytemplate.pt', |
| | | override_with='another.package:othertemplates/anothertemplate.pt') |
| | | import datetime |
| | | from zope.interface import implements |
| | | 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 |
| | | 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 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. |
| | | |
| | | The interface object used must be an instance of a class that inherits from |
| | | :class:`zope.interface.Interface`. |
| | | |
| | | 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. |
| | | |
| | | 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: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.override_resource(to_override='some.package', |
| | | override_with='another.package') |
| | | from zope.interface import directlyProvides |
| | | from zope.interface import Interface |
| | | |
| | | Individual subdirectories within a package can also be overridden: |
| | | class IBlogEntry(Interface): |
| | | pass |
| | | |
| | | .. ignore-next-block |
| | | 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 resource 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: |
| | | |
| | | 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 |
| | | |
| | | class IBlogEntry1(Interface): |
| | | pass |
| | | |
| | | 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 IBlogEntry2(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 BlogEntry(object): |
| | | def __init__(self, title, body, author): |
| | | self.title = title |
| | | self.body = body |
| | | self.author = author |
| | | self.created = datetime.datetime.now() |
| | | |
| | | 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. |
| | | entry = BlogEntry('title', 'body', 'author') |
| | | directlyProvides(entry, IBlogEntry1) |
| | | alsoProvides(entry, IBlogEntry2) |
| | | |
| | | 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: |
| | | :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. |
| | | |
| | | .. ignore-next-block |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.override_resource(to_override='.subpackage:templates/', |
| | | override_with='another.package:templates/') |
| | | |
| | | 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. |
| | | |
| | | 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 |
| | | |
| | | .. _resource_zcml_directive: |
| | | Defining a Resource Tree |
| | | ------------------------ |
| | | |
| | | The ``resource`` ZCML Directive |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | 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: |
| | | |
| | | 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``. |
| | | - 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. |
| | | |
| | | An individual :app:`Pyramid` ``resource`` ZCML statement can |
| | | override a single resource. For example: |
| | | - 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`. |
| | | |
| | | .. code-block:: xml |
| | | See :ref:`traversal_chapter` for more information about how traversal |
| | | works against resource instances. |
| | | |
| | | .. index:: |
| | | pair: location-aware; resource |
| | | |
| | | .. _location_aware: |
| | | |
| | | Location-Aware Resources |
| | | ------------------------ |
| | | |
| | | 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". |
| | | |
| | | 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 ``__parent__`` of the root resource should be ``None`` and its |
| | | ``__name__`` should be the empty string. For instance: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | <resource |
| | | to_override="some.package:templates/mytemplate.pt" |
| | | override_with="another.package:othertemplates/anothertemplate.pt" |
| | | /> |
| | | class MyRootResource(object): |
| | | __name__ = '' |
| | | __parent__ = None |
| | | |
| | | 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: |
| | | 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. |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | 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. |
| | | |
| | | <resource |
| | | to_override="some.package" |
| | | override_with="another.package" |
| | | /> |
| | | .. 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). |
| | | |
| | | Individual subdirectories within a package can also be overridden: |
| | | .. sidebar:: Using :mod:`pyramid_traversalwrapper` |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | 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. |
| | | |
| | | <resource |
| | | to_override="some.package:templates/" |
| | | override_with="another.package:othertemplates/" |
| | | /> |
| | | 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. |
| | | |
| | | 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. |
| | | 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``. |
| | | |
| | | 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: |
| | | .. index:: |
| | | single: resource API functions |
| | | single: url generation (traversal) |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | :app:`Pyramid` API Functions That Act Against Resources |
| | | ------------------------------------------------------- |
| | | |
| | | <resource |
| | | to_override=".subpackage:templates/" |
| | | override_with="another.package:templates/" |
| | | /> |
| | | 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. |
| | | |
| | | See also :ref:`resource_directive`. |
| | | 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. |
| | | |
| | | 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. |
| | | |
| | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | |
| | | :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): |
| | |
| | | |
| | | .. _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 |
| | |
| | | (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: |
| | |
| | | (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 |
| | |
| | | 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: |
| | |
| | | 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 |
| | |
| | | # 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: |
| | |
| | | # 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 |
| | |
| | | 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:: |
| | | |
| | |
| | | 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: |
| | |
| | | .. 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: |
| | |
| | | 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) |
| | |
| | | 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: |
| | |
| | | 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 |
| | |
| | | # -> http://example.com/images/logo.png |
| | | |
| | | .. index:: |
| | | single: static resource view |
| | | single: static assets view |
| | | |
| | | Advanced: Serving Static Resources Using a View Callable |
| | | -------------------------------------------------------- |
| | | Advanced: Serving Static Assets 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. |
| | | 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 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. |
| | | 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 |
| | |
| | | .. 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" (``@@``). |
| | | |
| | |
| | | file instead of treating relative paths as relative to the current |
| | | view module. See :ref:`mako_templates`. |
| | | |
| | | The path can alternately be a :term:`resource specification` in the form |
| | | The path can alternately be a :term:`asset specification` in the form |
| | | ``some.dotted.package_name:relative/path``. This makes it possible to |
| | | address template resources which live in another package. For example: |
| | | address template assets which live in another package. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | {'foo':1, 'bar':2}, |
| | | request=request) |
| | | |
| | | A resource specification points at a file within a Python *package*. |
| | | An asset specification points at a file within a Python *package*. |
| | | In this case, it points at a file named ``foo.pt`` within the |
| | | ``templates`` directory of the ``mypackage`` package. Using a |
| | | resource specification instead of a relative template name is usually |
| | | a good idea, because calls to ``render_to_response`` using resource |
| | | asset specification instead of a relative template name is usually |
| | | a good idea, because calls to ``render_to_response`` using asset |
| | | specifications will continue to work properly if you move the code |
| | | containing them around. |
| | | |
| | | .. note:: |
| | | |
| | | Mako templating system bindings also respect absolute resource |
| | | Mako templating system bindings also respect absolute asset |
| | | specifications as an argument to any of the ``render*`` commands. If a |
| | | template name defines a ``:`` (colon) character and is not an absolute |
| | | path, it is treated as an absolute resource specification. |
| | | path, it is treated as an absolute asset specification. |
| | | |
| | | In the examples above we pass in a keyword argument named ``request`` |
| | | representing the current :app:`Pyramid` request. Passing a request |
| | |
| | | :app:`Pyramid` bindings directly within view callables, the |
| | | auto-template-reload strategy explained in |
| | | :ref:`reload_templates_section` will not be available, nor will the |
| | | template resource overriding capability explained in |
| | | :ref:`overriding_resources_section` be available, nor will it be |
| | | template asset overriding capability explained in |
| | | :ref:`overriding_assets_section` be available, nor will it be |
| | | possible to use any template using that language as a |
| | | :term:`renderer`. However, it's reasonably easy to write custom |
| | | templating system binding packages for use under :app:`Pyramid` so |
| | |
| | | templating languages supported by :app:`Pyramid`. |
| | | |
| | | To use a renderer via view configuration, specify a template |
| | | :term:`resource specification` as the ``renderer`` argument, or |
| | | :term:`asset specification` as the ``renderer`` argument, or |
| | | attribute to the :term:`view configuration` of a :term:`view |
| | | callable`. Then return a *dictionary* from that view callable. The |
| | | dictionary items returned by the view callable will be made available |
| | |
| | | we're using a Chameleon renderer, it means "relative to the directory in |
| | | which the file which defines the view configuration lives". In this case, |
| | | this is the directory containing the file that defines the ``my_view`` |
| | | function. View-configuration-relative resource specifications work only |
| | | function. View-configuration-relative asset specifications work only |
| | | in Chameleon, not in Mako templates. |
| | | |
| | | Similar renderer configuration can be done imperatively and via |
| | |
| | | |
| | | Although a renderer path is usually just a simple relative pathname, a path |
| | | named as a renderer can be absolute, starting with a slash on UNIX or a drive |
| | | letter prefix on Windows. The path can alternately be a :term:`resource |
| | | letter prefix on Windows. The path can alternately be an :term:`asset |
| | | specification` in the form ``some.dotted.package_name:relative/path``, making |
| | | it possible to address template resources which live in another package. |
| | | it possible to address template assets which live in another package. |
| | | |
| | | Not just any template from any arbitrary templating system may be used as a |
| | | renderer. Bindings must exist specifically for :app:`Pyramid` to use a |
| | |
| | | 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 |
| | | :term:`renderer`: |
| | | 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 :term:`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 |
| | | :linenos: |
| | |
| | | request: <Request - at 0x1d2ecd0> |
| | | project: proj |
| | | macros: <Macros - at 0x1d3aed0> |
| | | context: <MyModel None at 0x1d39130> |
| | | context: <MyResource None at 0x1d39130> |
| | | view: <function my_view at 0x1d23570> |
| | | |
| | | NameError: wrong |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | |
| | | 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']``: |
| | | |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | 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 |
| | | |
| | |
| | | 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`. |
| | | 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 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. |
| | | .. note:: |
| | | |
| | | 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 |
| | | If the items contained within the resource tree are "persistent" (they |
| | | have state that lasts longer than the execution of a single process), they |
| | | become analogous to the concept of :term:`domain model` objects used by |
| | | many other frameworks. |
| | | |
| | | 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 |
| | |
| | | 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, |
| | |
| | | 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']``. |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | | |
| | |
| | | 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 |
| | | |
| | |
| | | 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. |
| | | |
| | |
| | | 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 |
| | |
| | | |
| | | 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. |
| | |
| | | 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 |
| | |
| | | :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. |
| | |
| | | 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`` |
| | |
| | | |
| | | ``view_renderer`` |
| | | This is either a single string term (e.g. ``json``) or a string |
| | | implying a path or :term:`resource specification` |
| | | implying a path or :term:`asset specification` |
| | | (e.g. ``templates/views.pt``). If the renderer value is a single |
| | | term (does not contain a dot ``.``), the specified term will be used |
| | | to look up a renderer implementation, and that renderer |
| | |
| | | |
| | | 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 |
| | |
| | | :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``). |
| | | 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 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. |
| | | 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 |
| | |
| | | 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:: |
| | | |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | |
| | | config.add_view('myproject.views.hello_world', |
| | | name='hello', |
| | | context='myproject.models.Hello', |
| | | context='myproject.resources.Hello', |
| | | renderer='json') |
| | | |
| | | |
| | |
| | | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | |
| | | 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 |
| | | |
| | |
| | | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | |
| | | 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. |
| | | |
| | | .. sidebar:: Resource Specifications |
| | | - 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. |
| | | |
| | | 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`. |
| | | .. sidebar:: Asset Specifications |
| | | |
| | | 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`: |
| | |
| | | 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. |
| | | ``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:`resource |
| | | specification` in the form |
| | | ``some.dotted.package_name:relative/path``, making it possible to |
| | | address template resources which live in a separate package. |
| | | 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 |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | .. 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} |
| | |
| | | :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: |
| | | |
| | |
| | | |
| | | 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. |
| | | |
| | |
| | | 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: |
| | |
| | | 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: |
| | |
| | | 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: |
| | |
| | | # 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 |
| | |
| | | # 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 |
| | |
| | | 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`` |
| | |
| | | Python 2.5.4 (r254:67916, Sep 4 2009, 02:12:16) |
| | | [GCC 4.2.1 (Apple Inc. build 5646)] on darwin |
| | | Type "help" for more information. "root" is the Pyramid app root object. |
| | | >>> from pyramid.traversal import model_path |
| | | >>> from pyramid.traversal import resource_path |
| | | >>> from myapp.models import Document |
| | | >>> root['name'] = Document('title') |
| | | >>> doc = root['name'] |
| | | >>> docid = root.catalog.document_map.add(model_path(doc)) |
| | | >>> docid = root.catalog.document_map.add(resource_path(doc)) |
| | | >>> root.catalog.index_doc(docid, doc) |
| | | >>> import transaction |
| | | >>> transaction.commit() |
| | |
| | | Content Models with ``models.py`` |
| | | --------------------------------- |
| | | |
| | | :app:`Pyramid` often uses the word :term:`model` when talking about |
| | | content resources arranged in the hierarchical *object graph* |
| | | consulted by :term:`traversal`. The ``models.py`` file is where the |
| | | ``pyramid_zodb`` Paster template put the classes that implement our |
| | | model objects. |
| | | :app:`Pyramid` uses the word :term:`resource` to describe objects arranged |
| | | hierarchically in a :term:`resource tree`. This tree is consulted by |
| | | :term:`traversal` to map URLs to code. In this application, the resource |
| | | tree represents the site structure, but it *also* represents the |
| | | :term:`domain model` of the application, because eeach resource is a node |
| | | stored persistently in a :term:`ZODB` database. The ``models.py`` file is |
| | | where the ``pyramid_zodb`` Paster template put the classes that implement our |
| | | resource objects, each of which happens also to be a domain model |
| | | object. |
| | | |
| | | Here is the source for ``models.py``: |
| | | |
| | |
| | | =============== |
| | | Defining Models |
| | | =============== |
| | | ========================= |
| | | Defining the Domain Model |
| | | ========================= |
| | | |
| | | The first change we'll make to our bone-stock ``paster`` -generated |
| | | application will be to define a number of :term:`model` constructors. |
| | | For this application, which will be a Wiki, we will need two kinds of |
| | | model constructors: a "Wiki" model constructor, and a "Page" model |
| | | constructor. Both our Page and Wiki constructors will be class |
| | | objects. A single instance of the "Wiki" class will serve as a |
| | | container for "Page" objects, which will be instances of the "Page" |
| | | class. |
| | | application will be to define a number of :term:`resource` constructors. |
| | | Remember that, because we're using :term:`ZODB` to represent our |
| | | :term:`resource tree`, each of these resource constructors represents a |
| | | :term:`domain model` object, so we'll call these constructors "model |
| | | constructors". For this application, which will be a Wiki, we will need two |
| | | kinds of model constructors: a "Wiki" model constructor, and a "Page" model |
| | | constructor. Both our Page and Wiki constructors will be class objects. A |
| | | single instance of the "Wiki" class will serve as a container for "Page" |
| | | objects, which will be instances of the "Page" class. |
| | | |
| | | The source code for this tutorial stage can be browsed via |
| | | `http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/models/ |
| | |
| | | The ``view_wiki`` view function |
| | | ------------------------------- |
| | | |
| | | The ``view_wiki`` function will be configured to respond as the |
| | | default view of a ``Wiki`` model object. It always redirects to the |
| | | ``Page`` object named "FrontPage". It returns an instance of the |
| | | :class:`pyramid.httpexceptions.HTTPFound` class (instances of which |
| | | implement the WebOb :term:`response` interface), and the |
| | | :func:`pyramid.url.model_url` API. :func:`pyramid.url.model_url` |
| | | constructs a URL to the ``FrontPage`` page |
| | | (e.g. ``http://localhost:6543/FrontPage``), and uses it as the |
| | | "location" of the HTTPFound response, forming an HTTP redirect. |
| | | The ``view_wiki`` function will be configured to respond as the default view |
| | | of a ``Wiki`` model object. It always redirects to the ``Page`` object named |
| | | "FrontPage". It returns an instance of the |
| | | :class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement |
| | | the WebOb :term:`response` interface), and the |
| | | :func:`pyramid.url.resource_url` API. :func:`pyramid.url.resource_url` |
| | | constructs a URL to the ``FrontPage`` page resource |
| | | (e.g. ``http://localhost:6543/FrontPage``), and uses it as the "location" of |
| | | the HTTPFound response, forming an HTTP redirect. |
| | | |
| | | The ``view_page`` view function |
| | | ------------------------------- |
| | |
| | | from pyramid.httpexceptions import HTTPFound |
| | | |
| | | from pyramid.view import view_config |
| | | from pyramid.url import model_url |
| | | from pyramid.url import resource_url |
| | | |
| | | from pyramid.security import remember |
| | | from pyramid.security import forget |
| | |
| | | |
| | | @view_config(context=Wiki, name='login', renderer='templates/login.pt') |
| | | def login(request): |
| | | login_url = model_url(request.context, request, 'login') |
| | | login_url = resource_url(request.context, request, 'login') |
| | | referrer = request.url |
| | | if referrer == login_url: |
| | | referrer = '/' # never use the login form itself as came_from |
| | |
| | | @view_config(context=Wiki, name='logout') |
| | | def logout(request): |
| | | headers = forget(request) |
| | | return HTTPFound(location = model_url(request.context, request), |
| | | return HTTPFound(location = resource_url(request.context, request), |
| | | headers = headers) |
| | | |
| | |
| | | class ViewWikiTests(unittest.TestCase): |
| | | def test_it(self): |
| | | from tutorial.views import view_wiki |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | response = view_wiki(context, request) |
| | | self.assertEqual(response.location, 'http://example.com/FrontPage') |
| | |
| | | return view_page(context, request) |
| | | |
| | | def test_it(self): |
| | | wiki = testing.DummyModel() |
| | | wiki['IDoExist'] = testing.DummyModel() |
| | | context = testing.DummyModel(data='Hello CruelWorld IDoExist') |
| | | wiki = testing.DummyResource() |
| | | wiki['IDoExist'] = testing.DummyResource() |
| | | context = testing.DummyResource(data='Hello CruelWorld IDoExist') |
| | | context.__parent__ = wiki |
| | | context.__name__ = 'thepage' |
| | | request = testing.DummyRequest() |
| | |
| | | return add_page(context, request) |
| | | |
| | | def test_it_notsubmitted(self): |
| | | from pyramid.url import model_url |
| | | context = testing.DummyModel() |
| | | from pyramid.url import resource_url |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | request.subpath = ['AnotherPage'] |
| | | info = self._callFUT(context, request) |
| | | self.assertEqual(info['page'].data,'') |
| | | self.assertEqual(info['save_url'], |
| | | model_url(context, request, 'add_page', 'AnotherPage')) |
| | | resource_url( |
| | | context, request, 'add_page', 'AnotherPage')) |
| | | |
| | | def test_it_submitted(self): |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest({'form.submitted':True, |
| | | 'body':'Hello yo!'}) |
| | | request.subpath = ['AnotherPage'] |
| | |
| | | return edit_page(context, request) |
| | | |
| | | def test_it_notsubmitted(self): |
| | | from pyramid.url import model_url |
| | | context = testing.DummyModel() |
| | | from pyramid.url import resource_url |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | info = self._callFUT(context, request) |
| | | self.assertEqual(info['page'], context) |
| | | self.assertEqual(info['save_url'], |
| | | model_url(context, request, 'edit_page')) |
| | | resource_url(context, request, 'edit_page')) |
| | | |
| | | def test_it_submitted(self): |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest({'form.submitted':True, |
| | | 'body':'Hello yo!'}) |
| | | response = self._callFUT(context, request) |
| | |
| | | import re |
| | | |
| | | from pyramid.httpexceptions import HTTPFound |
| | | from pyramid.url import model_url |
| | | from pyramid.url import resource_url |
| | | |
| | | from pyramid.security import authenticated_userid |
| | | |
| | |
| | | |
| | | @view_config(context=Wiki, permission='view') |
| | | def view_wiki(context, request): |
| | | return HTTPFound(location = model_url(context, request, 'FrontPage')) |
| | | return HTTPFound(location = resource_url(context, request, 'FrontPage')) |
| | | |
| | | @view_config(context=Page, renderer='templates/view.pt', permission='view') |
| | | def view_page(context, request): |
| | |
| | | word = match.group(1) |
| | | if word in wiki: |
| | | page = wiki[word] |
| | | view_url = model_url(page, request) |
| | | view_url = resource_url(page, request) |
| | | return '<a href="%s">%s</a>' % (view_url, word) |
| | | else: |
| | | add_url = request.application_url + '/add_page/' + word |
| | |
| | | |
| | | content = publish_parts(context.data, writer_name='html')['html_body'] |
| | | content = wikiwords.sub(check, content) |
| | | edit_url = model_url(context, request, 'edit_page') |
| | | edit_url = resource_url(context, request, 'edit_page') |
| | | |
| | | logged_in = authenticated_userid(request) |
| | | |
| | |
| | | page.__name__ = name |
| | | page.__parent__ = context |
| | | context[name] = page |
| | | return HTTPFound(location = model_url(page, request)) |
| | | save_url = model_url(context, request, 'add_page', name) |
| | | return HTTPFound(location = resource_url(page, request)) |
| | | save_url = resource_url(context, request, 'add_page', name) |
| | | page = Page('') |
| | | page.__name__ = name |
| | | page.__parent__ = context |
| | |
| | | def edit_page(context, request): |
| | | if 'form.submitted' in request.params: |
| | | context.data = request.params['body'] |
| | | return HTTPFound(location = model_url(context, request)) |
| | | return HTTPFound(location = resource_url(context, request)) |
| | | |
| | | logged_in = authenticated_userid(request) |
| | | |
| | | return dict(page = context, |
| | | save_url = model_url(context, request, 'edit_page'), |
| | | save_url = resource_url(context, request, 'edit_page'), |
| | | logged_in = logged_in) |
| | | |
| | |
| | | class ViewWikiTests(unittest.TestCase): |
| | | def test_it(self): |
| | | from tutorial.views import view_wiki |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | response = view_wiki(context, request) |
| | | self.assertEqual(response.location, 'http://example.com/FrontPage') |
| | |
| | | return view_page(context, request) |
| | | |
| | | def test_it(self): |
| | | wiki = testing.DummyModel() |
| | | wiki['IDoExist'] = testing.DummyModel() |
| | | context = testing.DummyModel(data='Hello CruelWorld IDoExist') |
| | | wiki = testing.DummyResource() |
| | | wiki['IDoExist'] = testing.DummyResource() |
| | | context = testing.DummyResource(data='Hello CruelWorld IDoExist') |
| | | context.__parent__ = wiki |
| | | context.__name__ = 'thepage' |
| | | request = testing.DummyRequest() |
| | |
| | | return add_page(context, request) |
| | | |
| | | def test_it_notsubmitted(self): |
| | | from pyramid.url import model_url |
| | | context = testing.DummyModel() |
| | | from pyramid.url import resource_url |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | request.subpath = ['AnotherPage'] |
| | | info = self._callFUT(context, request) |
| | | self.assertEqual(info['page'].data,'') |
| | | self.assertEqual(info['save_url'], |
| | | model_url(context, request, 'add_page', 'AnotherPage')) |
| | | resource_url( |
| | | context, request, 'add_page', 'AnotherPage')) |
| | | |
| | | def test_it_submitted(self): |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest({'form.submitted':True, |
| | | 'body':'Hello yo!'}) |
| | | request.subpath = ['AnotherPage'] |
| | |
| | | return edit_page(context, request) |
| | | |
| | | def test_it_notsubmitted(self): |
| | | from pyramid.url import model_url |
| | | context = testing.DummyModel() |
| | | from pyramid.url import resource_url |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | info = self._callFUT(context, request) |
| | | self.assertEqual(info['page'], context) |
| | | self.assertEqual(info['save_url'], |
| | | model_url(context, request, 'edit_page')) |
| | | resource_url(context, request, 'edit_page')) |
| | | |
| | | def test_it_submitted(self): |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest({'form.submitted':True, |
| | | 'body':'Hello yo!'}) |
| | | response = self._callFUT(context, request) |
| | |
| | | import re |
| | | |
| | | from pyramid.httpexceptions import HTTPFound |
| | | from pyramid.url import model_url |
| | | from pyramid.url import resource_url |
| | | from pyramid.view import view_config |
| | | |
| | | from tutorial.models import Page |
| | |
| | | |
| | | @view_config(context=Wiki) |
| | | def view_wiki(context, request): |
| | | return HTTPFound(location = model_url(context, request, 'FrontPage')) |
| | | return HTTPFound(location = resource_url(context, request, 'FrontPage')) |
| | | |
| | | @view_config(context=Page, renderer='templates/view.pt') |
| | | def view_page(context, request): |
| | |
| | | word = match.group(1) |
| | | if word in wiki: |
| | | page = wiki[word] |
| | | view_url = model_url(page, request) |
| | | view_url = resource_url(page, request) |
| | | return '<a href="%s">%s</a>' % (view_url, word) |
| | | else: |
| | | add_url = request.application_url + '/add_page/' + word |
| | |
| | | |
| | | content = publish_parts(context.data, writer_name='html')['html_body'] |
| | | content = wikiwords.sub(check, content) |
| | | edit_url = model_url(context, request, 'edit_page') |
| | | edit_url = resource_url(context, request, 'edit_page') |
| | | return dict(page = context, content = content, edit_url = edit_url) |
| | | |
| | | @view_config(context=Wiki, name='add_page', renderer='templates/edit.pt') |
| | |
| | | page.__name__ = name |
| | | page.__parent__ = context |
| | | context[name] = page |
| | | return HTTPFound(location = model_url(page, request)) |
| | | save_url = model_url(context, request, 'add_page', name) |
| | | return HTTPFound(location = resource_url(page, request)) |
| | | save_url = resource_url(context, request, 'add_page', name) |
| | | page = Page('') |
| | | page.__name__ = name |
| | | page.__parent__ = context |
| | |
| | | def edit_page(context, request): |
| | | if 'form.submitted' in request.params: |
| | | context.data = request.params['body'] |
| | | return HTTPFound(location = model_url(context, request)) |
| | | return HTTPFound(location = resource_url(context, request)) |
| | | |
| | | return dict(page = context, |
| | | save_url = model_url(context, request, 'edit_page')) |
| | | save_url = resource_url(context, request, 'edit_page')) |
| | | |
| | | |
| | |
| | | class ViewWikiTests(unittest.TestCase): |
| | | def test_it(self): |
| | | from tutorial.views import view_wiki |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | response = view_wiki(context, request) |
| | | self.assertEqual(response.location, 'http://example.com/FrontPage') |
| | |
| | | return view_page(context, request) |
| | | |
| | | def test_it(self): |
| | | wiki = testing.DummyModel() |
| | | wiki['IDoExist'] = testing.DummyModel() |
| | | context = testing.DummyModel(data='Hello CruelWorld IDoExist') |
| | | wiki = testing.DummyResource() |
| | | wiki['IDoExist'] = testing.DummyResource() |
| | | context = testing.DummyResource(data='Hello CruelWorld IDoExist') |
| | | context.__parent__ = wiki |
| | | context.__name__ = 'thepage' |
| | | request = testing.DummyRequest() |
| | |
| | | return add_page(context, request) |
| | | |
| | | def test_it_notsubmitted(self): |
| | | from pyramid.url import model_url |
| | | context = testing.DummyModel() |
| | | from pyramid.url import resource_url |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | request.subpath = ['AnotherPage'] |
| | | info = self._callFUT(context, request) |
| | | self.assertEqual(info['page'].data,'') |
| | | self.assertEqual( |
| | | info['save_url'], |
| | | model_url(context, request, 'add_page', 'AnotherPage')) |
| | | resource_url(context, request, 'add_page', 'AnotherPage')) |
| | | |
| | | def test_it_submitted(self): |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest({'form.submitted':True, |
| | | 'body':'Hello yo!'}) |
| | | request.subpath = ['AnotherPage'] |
| | |
| | | return edit_page(context, request) |
| | | |
| | | def test_it_notsubmitted(self): |
| | | from pyramid.url import model_url |
| | | context = testing.DummyModel() |
| | | from pyramid.url import resource_url |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest() |
| | | info = self._callFUT(context, request) |
| | | self.assertEqual(info['page'], context) |
| | | self.assertEqual(info['save_url'], |
| | | model_url(context, request, 'edit_page')) |
| | | resource_url(context, request, 'edit_page')) |
| | | |
| | | def test_it_submitted(self): |
| | | context = testing.DummyModel() |
| | | context = testing.DummyResource() |
| | | request = testing.DummyRequest({'form.submitted':True, |
| | | 'body':'Hello yo!'}) |
| | | response = self._callFUT(context, request) |
| | |
| | | import re |
| | | |
| | | from pyramid.httpexceptions import HTTPFound |
| | | from pyramid.url import model_url |
| | | from pyramid.url import resource_url |
| | | |
| | | from tutorial.models import Page |
| | | |
| | |
| | | wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") |
| | | |
| | | def view_wiki(context, request): |
| | | return HTTPFound(location = model_url(context, request, 'FrontPage')) |
| | | return HTTPFound(location = resource_url(context, request, 'FrontPage')) |
| | | |
| | | def view_page(context, request): |
| | | wiki = context.__parent__ |
| | |
| | | word = match.group(1) |
| | | if word in wiki: |
| | | page = wiki[word] |
| | | view_url = model_url(page, request) |
| | | view_url = resource_url(page, request) |
| | | return '<a href="%s">%s</a>' % (view_url, word) |
| | | else: |
| | | add_url = request.application_url + '/add_page/' + word |
| | |
| | | |
| | | content = publish_parts(context.data, writer_name='html')['html_body'] |
| | | content = wikiwords.sub(check, content) |
| | | edit_url = model_url(context, request, 'edit_page') |
| | | edit_url = resource_url(context, request, 'edit_page') |
| | | return dict(page = context, content = content, edit_url = edit_url) |
| | | |
| | | def add_page(context, request): |
| | |
| | | page.__name__ = name |
| | | page.__parent__ = context |
| | | context[name] = page |
| | | return HTTPFound(location = model_url(page, request)) |
| | | save_url = model_url(context, request, 'add_page', name) |
| | | return HTTPFound(location = resource_url(page, request)) |
| | | save_url = resource_url(context, request, 'add_page', name) |
| | | page = Page('') |
| | | page.__name__ = name |
| | | page.__parent__ = context |
| | |
| | | def edit_page(context, request): |
| | | if 'form.submitted' in request.params: |
| | | context.data = request.params['body'] |
| | | return HTTPFound(location = model_url(context, request)) |
| | | return HTTPFound(location = resource_url(context, request)) |
| | | |
| | | return dict(page = context, |
| | | save_url = model_url(context, request, 'edit_page')) |
| | | save_url = resource_url(context, request, 'edit_page')) |
| | | |
| | | |
| | |
| | | =============== |
| | | Defining Models |
| | | =============== |
| | | ========================= |
| | | Defining the Domain Model |
| | | ========================= |
| | | |
| | | The first change we'll make to our stock paster-generated application |
| | | will be to define a :term:`model` constructor representing a wiki |
| | | page. We'll do this inside our ``models.py`` file. |
| | | The first change we'll make to our stock paster-generated application will be |
| | | to define a :term:`domain model` constructor representing a wiki page. We'll |
| | | do this inside our ``models.py`` file. |
| | | |
| | | The source code for this tutorial stage can be browsed at |
| | | `http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/models/ |
| | |
| | | zcml/aclauthorizationpolicy |
| | | zcml/adapter |
| | | zcml/authtktauthenticationpolicy |
| | | zcml/asset |
| | | zcml/configure |
| | | zcml/default_permission |
| | | zcml/forbidden |
| | |
| | | zcml/remoteuserauthenticationpolicy |
| | | zcml/renderer |
| | | zcml/repozewho1authenticationpolicy |
| | | zcml/resource |
| | | zcml/route |
| | | zcml/scan |
| | | zcml/static |
| | |
| | | -------------------------- |
| | | |
| | | When this directive is used, authorization information is obtained |
| | | from :term:`ACL` objects attached to model instances. |
| | | from :term:`ACL` objects attached to :term:`resource` objects. |
| | | |
| | | Attributes |
| | | ~~~~~~~~~~ |
New file |
| | |
| | | .. _asset_directive: |
| | | |
| | | ``asset`` |
| | | --------- |
| | | |
| | | The ``asset`` directive adds an asset override for a single |
| | | static file/directory asset. |
| | | |
| | | Attributes |
| | | ~~~~~~~~~~ |
| | | |
| | | ``to_override`` |
| | | A :term:`asset specification` specifying the asset to be |
| | | overridden. |
| | | |
| | | ``override_with`` |
| | | A :term:`asset specification` specifying the asset which |
| | | is used as the override. |
| | | |
| | | Examples |
| | | ~~~~~~~~ |
| | | |
| | | .. topic:: Overriding a Single Asset File |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | |
| | | <asset |
| | | to_override="some.package:templates/mytemplate.pt" |
| | | override_with="another.package:othertemplates/anothertemplate.pt" |
| | | /> |
| | | |
| | | .. topic:: Overriding all Assets in a Package |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | |
| | | <asset |
| | | to_override="some.package" |
| | | override_with="another.package" |
| | | /> |
| | | |
| | | .. topic:: Overriding all Assets in a Subdirectory of a Package |
| | | |
| | | .. code-block:: xml |
| | | :linenos: |
| | | |
| | | <asset |
| | | to_override="some.package:templates/" |
| | | override_with="another.package:othertemplates/" |
| | | /> |
| | | |
| | | Alternatives |
| | | ~~~~~~~~~~~~ |
| | | |
| | | The :meth:`pyramid.config.Configurator.override_asset` |
| | | method can be used instead of the ``resource`` ZCML directive. |
| | | |
| | | This directive can also be invoked as the ``resource`` ZCML directive for |
| | | backwards compatibility purposes. |
| | | |
| | | See Also |
| | | ~~~~~~~~ |
| | | |
| | | See also :ref:`asset_zcml_directive`. |
| | |
| | | |
| | | ``renderer`` |
| | | This is either a single string term (e.g. ``json``) or a string |
| | | implying a path or :term:`resource specification` |
| | | implying a path or :term:`asset specification` |
| | | (e.g. ``templates/views.pt``) used when the view returns a |
| | | non-:term:`response` object. This attribute has the same meaning as |
| | | it would in the context of :ref:`view_directive`; see the |
| | |
| | | ``factory`` |
| | | The :term:`dotted Python name` to a function that will generate a |
| | | :app:`Pyramid` context object when the associated route matches. |
| | | e.g. ``mypackage.models.MyFactoryClass``. If this argument is not |
| | | e.g. ``mypackage.resources.MyResource``. If this argument is not |
| | | specified, a default root factory will be used. |
| | | |
| | | ``xhr`` |
| | |
| | | |
| | | ``renderer`` |
| | | This is either a single string term (e.g. ``json``) or a string |
| | | implying a path or :term:`resource specification` |
| | | implying a path or :term:`asset specification` |
| | | (e.g. ``templates/views.pt``) used when the view returns a |
| | | non-:term:`response` object. This attribute has the same meaning as |
| | | it would in the context of :ref:`view_directive`; see the |
| | |
| | | ``factory`` |
| | | The :term:`dotted Python name` to a function that will generate a |
| | | :app:`Pyramid` context object when this route matches. |
| | | e.g. ``mypackage.models.MyFactoryClass``. If this argument is not |
| | | e.g. ``mypackage.resources.MyResource``. If this argument is not |
| | | specified, a default root factory will be used. |
| | | |
| | | ``view`` |
| | |
| | | |
| | | ``view_renderer`` |
| | | This is either a single string term (e.g. ``json``) or a string |
| | | implying a path or :term:`resource specification` |
| | | implying a path or :term:`asset specification` |
| | | (e.g. ``templates/views.pt``). If the renderer value is a single |
| | | term (does not contain a dot ``.``), the specified term will be used |
| | | to look up a renderer implementation, and that renderer |
| | |
| | | |
| | | ``renderer`` |
| | | This is either a single string term (e.g. ``json``) or a string |
| | | implying a path or :term:`resource specification` |
| | | implying a path or :term:`asset specification` |
| | | (e.g. ``templates/views.pt``). If the renderer value is a single |
| | | term (does not contain a dot ``.``), the specified term will be used |
| | | to look up a renderer implementation, and that renderer |
| | |
| | | template named "foo.pt" is in the "templates" directory relative to |
| | | the directory in which the ZCML file is defined), 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 |
| | | 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 resources which live in a separate package. |
| | | 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 |
| | |
| | | representing the class that a graph traversal parent object of the |
| | | :term:`context` must be an instance of (or :term:`interface` that a |
| | | parent object must provide) in order for this view to be found and |
| | | called. Your models must be "location-aware" to use this feature. |
| | | called. Your resources must be "location-aware" to use this feature. |
| | | See :ref:`location_aware` for more information about |
| | | location-awareness. |
| | | |
| | |
| | | :linenos: |
| | | |
| | | <view |
| | | context=".models.MyModel" |
| | | context=".resources.MyResource" |
| | | view=".views.hello_world" |
| | | /> |
| | | |
| | |
| | | :linenos: |
| | | |
| | | <view |
| | | context=".models.MyModel" |
| | | context=".resources.MyResource" |
| | | view=".views.hello_world_post" |
| | | request_method="POST" |
| | | /> |
New file |
| | |
| | | import os |
| | | import pkg_resources |
| | | |
| | | from zope.interface import implements |
| | | |
| | | from pyramid.interfaces import IPackageOverrides |
| | | |
| | | from pyramid.path import package_path |
| | | from pyramid.path import package_name |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | class OverrideProvider(pkg_resources.DefaultProvider): |
| | | def __init__(self, module): |
| | | pkg_resources.DefaultProvider.__init__(self, module) |
| | | self.module_name = module.__name__ |
| | | |
| | | def _get_overrides(self): |
| | | reg = get_current_registry() |
| | | overrides = reg.queryUtility(IPackageOverrides, self.module_name) |
| | | return overrides |
| | | |
| | | def get_resource_filename(self, manager, resource_name): |
| | | """ Return a true filesystem path for resource_name, |
| | | co-ordinating the extraction with manager, if the resource |
| | | must be unpacked to the filesystem. |
| | | """ |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | filename = overrides.get_filename(resource_name) |
| | | if filename is not None: |
| | | return filename |
| | | return pkg_resources.DefaultProvider.get_resource_filename( |
| | | self, manager, resource_name) |
| | | |
| | | def get_resource_stream(self, manager, resource_name): |
| | | """ Return a readable file-like object for resource_name.""" |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | stream = overrides.get_stream(resource_name) |
| | | if stream is not None: |
| | | return stream |
| | | return pkg_resources.DefaultProvider.get_resource_stream( |
| | | self, manager, resource_name) |
| | | |
| | | def get_resource_string(self, manager, resource_name): |
| | | """ Return a string containing the contents of resource_name.""" |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | string = overrides.get_string(resource_name) |
| | | if string is not None: |
| | | return string |
| | | return pkg_resources.DefaultProvider.get_resource_string( |
| | | self, manager, resource_name) |
| | | |
| | | def has_resource(self, resource_name): |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | result = overrides.has_resource(resource_name) |
| | | if result is not None: |
| | | return result |
| | | return pkg_resources.DefaultProvider.has_resource( |
| | | self, resource_name) |
| | | |
| | | def resource_isdir(self, resource_name): |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | result = overrides.isdir(resource_name) |
| | | if result is not None: |
| | | return result |
| | | return pkg_resources.DefaultProvider.resource_isdir( |
| | | self, resource_name) |
| | | |
| | | def resource_listdir(self, resource_name): |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | result = overrides.listdir(resource_name) |
| | | if result is not None: |
| | | return result |
| | | return pkg_resources.DefaultProvider.resource_listdir( |
| | | self, resource_name) |
| | | |
| | | class PackageOverrides: |
| | | implements(IPackageOverrides) |
| | | # pkg_resources arg in kw args below for testing |
| | | def __init__(self, package, pkg_resources=pkg_resources): |
| | | if hasattr(package, '__loader__') and not isinstance(package.__loader__, |
| | | self.__class__): |
| | | raise TypeError('Package %s already has a non-%s __loader__ ' |
| | | '(probably a module in a zipped egg)' % |
| | | (package, self.__class__)) |
| | | # We register ourselves as a __loader__ *only* to support the |
| | | # setuptools _find_adapter adapter lookup; this class doesn't |
| | | # actually support the PEP 302 loader "API". This is |
| | | # excusable due to the following statement in the spec: |
| | | # ... Loader objects are not |
| | | # required to offer any useful functionality (any such functionality, |
| | | # such as the zipimport get_data() method mentioned above, is |
| | | # optional)... |
| | | # A __loader__ attribute is basically metadata, and setuptools |
| | | # uses it as such. |
| | | package.__loader__ = self |
| | | # we call register_loader_type for every instantiation of this |
| | | # class; that's OK, it's idempotent to do it more than once. |
| | | pkg_resources.register_loader_type(self.__class__, OverrideProvider) |
| | | self.overrides = [] |
| | | self.overridden_package_name = package.__name__ |
| | | |
| | | def insert(self, path, package, prefix): |
| | | if not path or path.endswith('/'): |
| | | override = DirectoryOverride(path, package, prefix) |
| | | else: |
| | | override = FileOverride(path, package, prefix) |
| | | self.overrides.insert(0, override) |
| | | return override |
| | | |
| | | def search_path(self, resource_name): |
| | | for override in self.overrides: |
| | | o = override(resource_name) |
| | | if o is not None: |
| | | package, name = o |
| | | yield package, name |
| | | |
| | | def get_filename(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_filename(package, rname) |
| | | |
| | | def get_stream(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_stream(package, rname) |
| | | |
| | | def get_string(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_string(package, rname) |
| | | |
| | | def has_resource(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return True |
| | | |
| | | def isdir(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_isdir(package, rname) |
| | | |
| | | def listdir(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_listdir(package, rname) |
| | | |
| | | |
| | | class DirectoryOverride: |
| | | def __init__(self, path, package, prefix): |
| | | self.path = path |
| | | self.package = package |
| | | self.prefix = prefix |
| | | self.pathlen = len(self.path) |
| | | |
| | | def __call__(self, resource_name): |
| | | if resource_name.startswith(self.path): |
| | | name = '%s%s' % (self.prefix, resource_name[self.pathlen:]) |
| | | return self.package, name |
| | | |
| | | class FileOverride: |
| | | def __init__(self, path, package, prefix): |
| | | self.path = path |
| | | self.package = package |
| | | self.prefix = prefix |
| | | |
| | | def __call__(self, resource_name): |
| | | if resource_name == self.path: |
| | | return self.package, self.prefix |
| | | |
| | | def resolve_asset_spec(spec, pname='__main__'): |
| | | if pname and not isinstance(pname, basestring): |
| | | pname = pname.__name__ # as package |
| | | if os.path.isabs(spec): |
| | | return None, spec |
| | | filename = spec |
| | | if ':' in spec: |
| | | pname, filename = spec.split(':', 1) |
| | | elif pname is None: |
| | | pname, filename = None, spec |
| | | return pname, filename |
| | | |
| | | def asset_spec_from_abspath(abspath, package): |
| | | """ Try to convert an absolute path to a resource in a package to |
| | | a resource specification if possible; otherwise return the |
| | | absolute path. """ |
| | | if getattr(package, '__name__', None) == '__main__': |
| | | return abspath |
| | | pp = package_path(package) + os.path.sep |
| | | if abspath.startswith(pp): |
| | | relpath = abspath[len(pp):] |
| | | return '%s:%s' % (package_name(package), |
| | | relpath.replace(os.path.sep, '/')) |
| | | return abspath |
| | | |
| | | def abspath_from_asset_spec(spec, pname='__main__'): |
| | | if pname is None: |
| | | return spec |
| | | pname, filename = resolve_asset_spec(spec, pname) |
| | | if pname is None: |
| | | return filename |
| | | return pkg_resources.resource_filename(pname, filename) |
| | |
| | | permits access, return an instance of |
| | | :class:`pyramid.security.ACLDenied` if not.""" |
| | | |
| | | acl = '<No ACL found on any object in model lineage>' |
| | | acl = '<No ACL found on any object in resource lineage>' |
| | | |
| | | for location in lineage(context): |
| | | try: |
| | |
| | | """ Return a callable object which can be used to render a |
| | | :term:`Chameleon` text template using the template implied by the |
| | | ``path`` argument. The ``path`` argument may be a |
| | | package-relative path, an absolute path, or a :term:`resource |
| | | package-relative path, an absolute path, or a :term:`asset |
| | | specification`. |
| | | |
| | | .. warning:: This API is deprecated in :app:`Pyramid` 1.0. Use |
| | |
| | | """ Return the underyling object representing a :term:`Chameleon` |
| | | text template using the template implied by the ``path`` argument. |
| | | The ``path`` argument may be a package-relative path, an absolute |
| | | path, or a :term:`resource specification`. |
| | | path, or a :term:`asset specification`. |
| | | |
| | | .. warning:: This API is deprecated in :app:`Pyramid` 1.0. Use |
| | | the ``implementation()`` method of a template renderer retrieved via |
| | |
| | | def render_template(path, **kw): |
| | | """ Render a :term:`Chameleon` text template using the template |
| | | implied by the ``path`` argument. The ``path`` argument may be a |
| | | package-relative path, an absolute path, or a :term:`resource |
| | | package-relative path, an absolute path, or a :term:`asset |
| | | specification`. The arguments in ``*kw`` are passed as top-level |
| | | names to the template, and so may be used within the template |
| | | itself. Returns a string. |
| | |
| | | def render_template_to_response(path, **kw): |
| | | """ Render a :term:`Chameleon` text template using the template |
| | | implied by the ``path`` argument. The ``path`` argument may be a |
| | | package-relative path, an absolute path, or a :term:`resource |
| | | package-relative path, an absolute path, or a :term:`asset |
| | | specification`. The arguments in ``*kw`` are passed as top-level |
| | | names to the template, and so may be used within the template |
| | | itself. Returns a :term:`Response` object with the body as the |
| | |
| | | """ Return a callable object which can be used to render a |
| | | :term:`Chameleon` ZPT template using the template implied by the |
| | | ``path`` argument. The ``path`` argument may be a |
| | | package-relative path, an absolute path, or a :term:`resource |
| | | package-relative path, an absolute path, or a :term:`asset |
| | | specification`. |
| | | |
| | | .. warning:: This API is deprecated in :app:`Pyramid` 1.0. Use |
| | |
| | | """ Return the underyling object representing a :term:`Chameleon` |
| | | ZPT template using the template implied by the ``path`` argument. |
| | | The ``path`` argument may be a package-relative path, an absolute |
| | | path, or a :term:`resource specification`. |
| | | path, or a :term:`asset specification`. |
| | | |
| | | .. warning:: This API is deprecated in :app:`Pyramid` 1.0. Use |
| | | the ``implementation()`` method of a template renderer retrieved via |
| | |
| | | def render_template(path, **kw): |
| | | """ Render a :term:`Chameleon` ZPT template using the template |
| | | implied by the ``path`` argument. The ``path`` argument may be a |
| | | package-relative path, an absolute path, or a :term:`resource |
| | | package-relative path, an absolute path, or a :term:`asset |
| | | specification`. The arguments in ``*kw`` are passed as top-level |
| | | names to the template, and so may be used within the template |
| | | itself. Returns a string. |
| | |
| | | def render_template_to_response(path, **kw): |
| | | """ Render a :term:`Chameleon` ZPT template using the template |
| | | implied by the ``path`` argument. The ``path`` argument may be a |
| | | package-relative path, an absolute path, or a :term:`resource |
| | | package-relative path, an absolute path, or a :term:`asset |
| | | specification`. The arguments in ``*kw`` are passed as top-level |
| | | names to the template, and so may be used within the template |
| | | itself. Returns a :term:`Response` object with the body as the |
| | |
| | | from pyramid.registry import Registry |
| | | from pyramid.renderers import RendererHelper |
| | | from pyramid.request import route_request_iface |
| | | from pyramid.resource import PackageOverrides |
| | | from pyramid.resource import resolve_resource_spec |
| | | from pyramid.asset import PackageOverrides |
| | | from pyramid.asset import resolve_asset_spec |
| | | from pyramid.settings import Settings |
| | | from pyramid.static import StaticURLInfo |
| | | from pyramid.threadlocal import get_current_registry |
| | |
| | | self.action(IAuthorizationPolicy, None) |
| | | |
| | | def _make_spec(self, path_or_spec): |
| | | package, filename = resolve_resource_spec(path_or_spec, |
| | | self.package_name) |
| | | package, filename = resolve_asset_spec(path_or_spec, |
| | | self.package_name) |
| | | if package is None: |
| | | return filename # absolute filename |
| | | return '%s:%s' % (package, filename) |
| | | |
| | | def _split_spec(self, path_or_spec): |
| | | return resolve_resource_spec(path_or_spec, self.package_name) |
| | | return resolve_asset_spec(path_or_spec, self.package_name) |
| | | |
| | | def _derive_view(self, view, permission=None, predicates=(), |
| | | attr=None, renderer=None, wrapper_viewname=None, |
| | |
| | | this Configurator's constructor.""" |
| | | return self.name_resolver.maybe_resolve(dotted) |
| | | |
| | | def absolute_resource_spec(self, relative_spec): |
| | | """ Resolve the potentially relative :term:`resource |
| | | def absolute_asset_spec(self, relative_spec): |
| | | """ Resolve the potentially relative :term:`asset |
| | | specification` string passed as ``relative_spec`` into an |
| | | absolute resource specification string and return the string. |
| | | absolute asset specification string and return the string. |
| | | Use the ``package`` of this configurator as the package to |
| | | which the resource specification will be considered relative |
| | | when generating an absolute resource specification. If the |
| | | which the asset specification will be considered relative |
| | | when generating an absolute asset specification. If the |
| | | provided ``relative_spec`` argument is already absolute, or if |
| | | the ``relative_spec`` is not a string, it is simply returned.""" |
| | | if not isinstance(relative_spec, basestring): |
| | | return relative_spec |
| | | return self._make_spec(relative_spec) |
| | | |
| | | absolute_resource_spec = absolute_asset_spec # b/w compat forever |
| | | |
| | | def setup_registry(self, settings=None, root_factory=None, |
| | | authentication_policy=None, authorization_policy=None, |
| | |
| | | def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): |
| | | """ Load configuration from a :term:`ZCML` file into the |
| | | current configuration state. The ``spec`` argument is an |
| | | absolute filename, a relative filename, or a :term:`resource |
| | | absolute filename, a relative filename, or a :term:`asset |
| | | specification`, defaulting to ``configure.zcml`` (relative to |
| | | the package of the configurator's caller).""" |
| | | package_name, filename = self._split_spec(spec) |
| | |
| | | renderer |
| | | |
| | | This is either a single string term (e.g. ``json``) or a |
| | | string implying a path or :term:`resource specification` |
| | | 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 |
| | |
| | | current :term:`package` of the Configurator), 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 |
| | | :term:`asset specification` in the form |
| | | ``some.dotted.package_name:relative/path``, making it |
| | | possible to address template resources which live in a |
| | | possible to address template assets which live in a |
| | | separate package. |
| | | |
| | | The ``renderer`` attribute is optional. If it is not |
| | |
| | | |
| | | factory |
| | | |
| | | A Python object (often a function or a class) or a |
| | | :term:`dotted Python name` which refers to the same object |
| | | that will generate a :app:`Pyramid` :term:`context` |
| | | object when this route matches. For example, |
| | | ``mypackage.models.MyFactoryClass``. If this argument is |
| | | A Python object (often a function or a class) or a :term:`dotted |
| | | Python name` which refers to the same object that will generate a |
| | | :app:`Pyramid` root resource object when this route matches. For |
| | | example, ``mypackage.resources.MyFactory``. If this argument is |
| | | not specified, a default root factory will be used. |
| | | |
| | | traverse |
| | |
| | | view_renderer |
| | | |
| | | This is either a single string term (e.g. ``json``) or a |
| | | string implying a path or :term:`resource specification` |
| | | string implying a path or :term:`asset specification` |
| | | (e.g. ``templates/views.pt``). If the renderer value is a |
| | | single term (does not contain a dot ``.``), the specified |
| | | term will be used to look up a renderer implementation, and |
| | |
| | | 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 ' |
| | | raise ConfigurationError('You cannot override an asset with ' |
| | | 'itself') |
| | | |
| | | package = to_override |
| | |
| | | to_package = sys.modules[override_package] |
| | | 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, |
| | |
| | | """ Add one or more :term:`translation directory` paths to the |
| | | current configuration state. The ``specs`` argument is a |
| | | sequence that may contain absolute directory paths |
| | | (e.g. ``/usr/share/locale``) or :term:`resource specification` |
| | | (e.g. ``/usr/share/locale``) or :term:`asset specification` |
| | | names naming a directory path (e.g. ``some.package:locale``) |
| | | or a combination of the two. |
| | | |
| | |
| | | |
| | | @action_method |
| | | def add_static_view(self, name, path, **kw): |
| | | """ Add a view used to render static resources such as images |
| | | """ Add a view used to render static assets such as images |
| | | and CSS files. |
| | | |
| | | The ``name`` argument is a string representing :term:`view |
| | |
| | | |
| | | The ``path`` argument is the path on disk where the static |
| | | files reside. This can be an absolute path, a |
| | | package-relative path, or a :term:`resource specification`. |
| | | package-relative path, or a :term:`asset specification`. |
| | | |
| | | The ``cache_max_age`` keyword argument is input to set the |
| | | ``Expires`` and ``Cache-Control`` headers for static resources |
| | | ``Expires`` and ``Cache-Control`` headers for static assets |
| | | served. Note that this argument has no effect when the |
| | | ``name`` is a *url prefix*. By default, this argument is |
| | | ``None``, meaning that no particular Expires or Cache-Control |
| | | headers are set in the response. |
| | | |
| | | The ``permission`` keyword argument is used to specify the |
| | | :term:`permission` required by a user to execute the static |
| | | view. By default, it is the string |
| | | ``__no_permission_required__``. The |
| | | ``__no_permission_required__`` string is a special sentinel |
| | | which indicates that, even if a :term:`default permission` |
| | | exists for the current application, the static view should be |
| | | renderered to completely anonymous users. This default value |
| | | is permissive because, in most web apps, static resources |
| | | seldom need protection from viewing. |
| | | :term:`permission` required by a user to execute the static view. By |
| | | default, it is the string ``__no_permission_required__``. The |
| | | ``__no_permission_required__`` string is a special sentinel which |
| | | indicates that, even if a :term:`default permission` exists for the |
| | | current application, the static view should be renderered to |
| | | completely anonymous users. This default value is permissive |
| | | because, in most web apps, static assets seldom need protection from |
| | | viewing. |
| | | |
| | | *Usage* |
| | | |
| | | The ``add_static_view`` function is typically used in |
| | | conjunction with the :func:`pyramid.url.static_url` |
| | | function. ``add_static_view`` adds a view which renders a |
| | | static resource when some URL is visited; |
| | | static asset when some URL is visited; |
| | | :func:`pyramid.url.static_url` generates a URL to that |
| | | resource. |
| | | asset. |
| | | |
| | | The ``name`` argument to ``add_static_view`` is usually a |
| | | :term:`view name`. When this is the case, the |
| | | :func:`pyramid.url.static_url` API will generate a URL |
| | | which points to a Pyramid view, which will serve up a set of |
| | | resources that live in the package itself. For example: |
| | | assets that live in the package itself. For example: |
| | | |
| | | .. code-block:: python |
| | | |
| | |
| | | be served. |
| | | |
| | | ``add_static_view`` can alternately be used with a ``name`` |
| | | argument which is a *URL*, causing static resources to be |
| | | argument which is a *URL*, causing static assets to be |
| | | served from an external webserver. This happens when the |
| | | ``name`` argument is a URL (detected as any string with a |
| | | slash in it). In this mode, the ``name`` is used as the URL |
| | |
| | | listening on ``example.com`` must be itself configured to |
| | | respond properly to such a request. |
| | | |
| | | See :ref:`static_resources_section` for more information. |
| | | See :ref:`static_assets_section` for more information. |
| | | """ |
| | | spec = self._make_spec(path) |
| | | info = self.registry.queryUtility(IStaticURLInfo) |
| | |
| | | self.registry.registerUtility(policy, IAuthorizationPolicy) |
| | | self.registry.registerUtility(policy, IAuthenticationPolicy) |
| | | |
| | | def testing_models(self, models): |
| | | def testing_resources(self, resources): |
| | | """Unit/integration testing helper: registers a dictionary of |
| | | :term:`model` objects that can be resolved via the |
| | | :func:`pyramid.traversal.find_model` API. |
| | | :term:`resource` objects that can be resolved via the |
| | | :func:`pyramid.traversal.find_resource` API. |
| | | |
| | | The :func:`pyramid.traversal.find_model` API is called with |
| | | The :func:`pyramid.traversal.find_resource` API is called with |
| | | a path as one of its arguments. If the dictionary you |
| | | register when calling this method contains that path as a |
| | | string key (e.g. ``/foo/bar`` or ``foo/bar``), the |
| | | corresponding value will be returned to ``find_model`` (and |
| | | corresponding value will be returned to ``find_resource`` (and |
| | | thus to your code) when |
| | | :func:`pyramid.traversal.find_model` is called with an |
| | | :func:`pyramid.traversal.find_resource` is called with an |
| | | equivalent path string or tuple. |
| | | """ |
| | | class DummyTraverserFactory: |
| | |
| | | |
| | | def __call__(self, request): |
| | | path = request['PATH_INFO'] |
| | | ob = models[path] |
| | | ob = resources[path] |
| | | traversed = traversal_path(path) |
| | | return {'context':ob, 'view_name':'','subpath':(), |
| | | 'traversed':traversed, 'virtual_root':ob, |
| | | 'virtual_root_path':(), 'root':ob} |
| | | self.registry.registerAdapter(DummyTraverserFactory, (Interface,), |
| | | ITraverser) |
| | | return models |
| | | return resources |
| | | |
| | | testing_models = testing_resources # b/w compat |
| | | |
| | | @action_method |
| | | def testing_add_subscriber(self, event_iface=None): |
| | |
| | | def testing_add_renderer(self, path, renderer=None): |
| | | """Unit/integration testing helper: register a renderer at |
| | | ``path`` (usually a relative filename ala ``templates/foo.pt`` |
| | | or a resource specification) and return the renderer object. |
| | | or an asset specification) and return the renderer object. |
| | | If the ``renderer`` argument is None, a 'dummy' renderer will |
| | | be used. This function is useful when testing code that calls |
| | | the :func:`pyramid.renderers.render` function or |
| | |
| | | if traverse is not None: |
| | | # ``traverse`` can only be used as a *route* "predicate"; it |
| | | # adds 'traverse' to the matchdict if it's specified in the |
| | | # routing args. This causes the ModelGraphTraverser to use |
| | | # routing args. This causes the ResourceTreeTraverser to use |
| | | # the resolved traverse pattern as the traversal path. |
| | | from pyramid.urldispatch import _compile_route |
| | | _, tgenerate = _compile_route(traverse) |
| | |
| | | /> |
| | | |
| | | <meta:directive |
| | | name="asset" |
| | | schema="pyramid.zcml.IAssetDirective" |
| | | handler="pyramid.zcml.asset" |
| | | /> |
| | | |
| | | <meta:directive |
| | | name="resource" |
| | | schema="pyramid.zcml.IResourceDirective" |
| | | handler="pyramid.zcml.resource" |
| | | schema="pyramid.zcml.IAssetDirective" |
| | | handler="pyramid.zcml.asset" |
| | | /> |
| | | |
| | | <meta:directive |
| | |
| | | """ Return a set of principal identifiers allowed by the permission """ |
| | | |
| | | class IStaticURLInfo(Interface): |
| | | """ A policy for generating URLs to static resources """ |
| | | """ A policy for generating URLs to static assets """ |
| | | def add(name, spec, **extra): |
| | | """ Add a new static info registration """ |
| | | |
| | |
| | | # |
| | | ############################################################################## |
| | | |
| | | def inside(model1, model2): |
| | | """Is ``model1`` 'inside' ``model2``? Return ``True`` if so, else |
| | | def inside(resource1, resource2): |
| | | """Is ``resource1`` 'inside' ``resource2``? Return ``True`` if so, else |
| | | ``False``. |
| | | |
| | | ``model1`` is 'inside' ``model2`` if ``model2`` is a |
| | | :term:`lineage` ancestor of ``model1``. It is a lineage ancestor |
| | | ``resource1`` is 'inside' ``resource2`` if ``resource2`` is a |
| | | :term:`lineage` ancestor of ``resource1``. It is a lineage ancestor |
| | | if its parent (or one of its parent's parents, etc.) is an |
| | | ancestor. |
| | | """ |
| | | while model1 is not None: |
| | | if model1 is model2: |
| | | while resource1 is not None: |
| | | if resource1 is resource2: |
| | | return True |
| | | model1 = model1.__parent__ |
| | | resource1 = resource1.__parent__ |
| | | |
| | | return False |
| | | |
| | | def lineage(model): |
| | | def lineage(resource): |
| | | """ |
| | | Return a generator representing the :term:`lineage` of the |
| | | :term:`model` object implied by the ``model`` argument. The |
| | | generator first returns ``model`` unconditionally. Then, if |
| | | ``model`` supplies a ``__parent__`` attribute, return the object |
| | | represented by ``model.__parent__``. If *that* object has a |
| | | :term:`resource` object implied by the ``resource`` argument. The |
| | | generator first returns ``resource`` unconditionally. Then, if |
| | | ``resource`` supplies a ``__parent__`` attribute, return the object |
| | | represented by ``resource.__parent__``. If *that* object has a |
| | | ``__parent__`` attribute, return that object's parent, and so on, |
| | | until the object being inspected either has no ``__parent__`` |
| | | attribute or which has a ``__parent__`` attribute of ``None``. |
| | |
| | | list(lineage(thing2)) |
| | | [ <Thing object at thing2>, <Thing object at thing1> ] |
| | | """ |
| | | while model is not None: |
| | | yield model |
| | | while resource is not None: |
| | | yield resource |
| | | # The common case is that the AttributeError exception below |
| | | # is exceptional as long as the developer is a "good citizen" |
| | | # who has a root object with a __parent__ of None. Using an |
| | |
| | | # called in any non-trivial application over and over again to |
| | | # generate URLs and paths. |
| | | try: |
| | | model = model.__parent__ |
| | | resource = resource.__parent__ |
| | | except AttributeError: |
| | | model = None |
| | | resource = None |
| | | |
| | |
| | | from zope.interface import implements |
| | | from zope.interface import Interface |
| | | |
| | | from pyramid.interfaces import ITemplateRenderer |
| | | from pyramid.asset import resolve_asset_spec |
| | | from pyramid.asset import abspath_from_asset_spec |
| | | from pyramid.exceptions import ConfigurationError |
| | | from pyramid.resource import resolve_resource_spec |
| | | from pyramid.resource import abspath_from_resource_spec |
| | | from pyramid.interfaces import ITemplateRenderer |
| | | from pyramid.settings import asbool |
| | | from pyramid.util import DottedNameResolver |
| | | |
| | |
| | | pass |
| | | |
| | | class PkgResourceTemplateLookup(TemplateLookup): |
| | | """TemplateLookup subclass that handles resource specification |
| | | uri's""" |
| | | """TemplateLookup subclass that handles asset specification URIs""" |
| | | def adjust_uri(self, uri, relativeto): |
| | | """Called from within a Mako template, avoids adjusting the |
| | | uri if it looks like a resource specification""" |
| | | # Don't adjust pkg resource spec names |
| | | uri if it looks like an asset specification""" |
| | | # Don't adjust asset spec names |
| | | if ':' in uri: |
| | | return uri |
| | | return TemplateLookup.adjust_uri(self, uri, relativeto) |
| | |
| | | for it |
| | | |
| | | In addition to the basic filesystem lookup, this subclass will |
| | | use pkg_resource to load a file using the resource |
| | | use pkg_resource to load a file using the asset |
| | | specification syntax. |
| | | |
| | | """ |
| | |
| | | else: |
| | | return self._collection[uri] |
| | | except KeyError: |
| | | pname, path = resolve_resource_spec(uri) |
| | | srcfile = abspath_from_resource_spec(path, pname) |
| | | pname, path = resolve_asset_spec(uri) |
| | | srcfile = abspath_from_asset_spec(path, pname) |
| | | if os.path.isfile(srcfile): |
| | | return self._load(srcfile, uri) |
| | | raise exceptions.TopLevelLookupException( |
| | |
| | | 'Mako template used without a ``mako.directories`` setting') |
| | | if not hasattr(directories, '__iter__'): |
| | | directories = filter(None, directories.splitlines()) |
| | | directories = [ abspath_from_resource_spec(d) for d in directories ] |
| | | directories = [ abspath_from_asset_spec(d) for d in directories ] |
| | | if module_directory is not None: |
| | | module_directory = abspath_from_resource_spec(module_directory) |
| | | module_directory = abspath_from_asset_spec(module_directory) |
| | | if error_handler is not None: |
| | | dotted = DottedNameResolver(info.package) |
| | | error_handler = dotted.maybe_resolve(error_handler) |
| | |
| | | from pyramid.config import Configurator |
| | | from {{package}}.models import get_root |
| | | from {{package}}.resources import Root |
| | | |
| | | def main(global_config, **settings): |
| | | """ This function returns a Pyramid WSGI application. |
| | | """ |
| | | config = Configurator(root_factory=get_root, settings=settings) |
| | | config = Configurator(root_factory=Root, settings=settings) |
| | | config.add_view('{{package}}.views.my_view', |
| | | context='{{package}}.models.MyModel', |
| | | context='{{package}}:resources.Root" |
| | | renderer='{{package}}:templates/mytemplate.pt') |
| | | config.add_static_view('static', '{{package}}:static') |
| | | return config.make_wsgi_app() |
New file |
| | |
| | | class Root(object): |
| | | def __init__(self, request): |
| | | self.request = request |
| | |
| | | from pyramid.config import Configurator |
| | | from {{package}}.models import get_root |
| | | from {{package}}.models import Root |
| | | |
| | | def main(global_config, **settings): |
| | | """ This function returns a Pyramid WSGI application. |
| | | """ |
| | | zcml_file = settings.get('configure_zcml', 'configure.zcml') |
| | | config = Configurator(root_factory=get_root, settings=settings) |
| | | config = Configurator(root_factory=Root, settings=settings) |
| | | config.load_zcml(zcml_file) |
| | | return config.make_wsgi_app() |
| | | |
| | |
| | | <include package="pyramid.includes" /> |
| | | |
| | | <view |
| | | context=".models.MyModel" |
| | | context=".resources.Root" |
| | | view=".views.my_view" |
| | | renderer="templates/mytemplate.pt" |
| | | /> |
New file |
| | |
| | | class Root(object): |
| | | def __init__(self, request): |
| | | self.request = request |
| | |
| | | from pyramid.interfaces import ITemplateRenderer |
| | | from pyramid.interfaces import IRendererInfo |
| | | |
| | | from pyramid.asset import asset_spec_from_abspath |
| | | from pyramid.compat import json |
| | | from pyramid.decorator import reify |
| | | from pyramid.events import BeforeRender |
| | | from pyramid.path import caller_package |
| | | from pyramid.path import package_path |
| | | from pyramid.response import Response |
| | | from pyramid.resource import resource_spec_from_abspath |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | # API |
| | |
| | | |
| | | If the renderer name refers to a file on disk (such as when the |
| | | renderer is a template), it's usually best to supply the name as a |
| | | :term:`resource specification` |
| | | :term:`asset specification` |
| | | (e.g. ``packagename:path/to/template.pt``). |
| | | |
| | | You may supply a relative resource spec as ``renderer_name``. If |
| | | You may supply a relative asset spec as ``renderer_name``. If |
| | | the ``package`` argument is supplied, a relative renderer path |
| | | will be converted to an absolute resource specification by |
| | | will be converted to an absolute asset specification by |
| | | combining the package supplied as ``package`` with the relative |
| | | resource specification supplied as ``renderer_name``. If you do |
| | | asset specification supplied as ``renderer_name``. If you do |
| | | not supply a ``package`` (or ``package`` is ``None``) the package |
| | | name of the *caller* of this function will be used as the package. |
| | | |
| | |
| | | |
| | | If the renderer name refers to a file on disk (such as when the |
| | | renderer is a template), it's usually best to supply the name as a |
| | | :term:`resource specification`. |
| | | :term:`asset specification`. |
| | | |
| | | You may supply a relative resource spec as ``renderer_name``. If |
| | | You may supply a relative asset spec as ``renderer_name``. If |
| | | the ``package`` argument is supplied, a relative renderer name |
| | | will be converted to an absolute resource specification by |
| | | will be converted to an absolute asset specification by |
| | | combining the package supplied as ``package`` with the relative |
| | | resource specification supplied as ``renderer_name``. If you do |
| | | asset specification supplied as ``renderer_name``. If you do |
| | | not supply a ``package`` (or ``package`` is ``None``) the package |
| | | name of the *caller* of this function will be used as the package. |
| | | |
| | |
| | | """ Return the renderer object for the renderer named as |
| | | ``renderer_name``. |
| | | |
| | | You may supply a relative resource spec as ``renderer_name``. If |
| | | You may supply a relative asset spec as ``renderer_name``. If |
| | | the ``package`` argument is supplied, a relative renderer name |
| | | will be converted to an absolute resource specification by |
| | | will be converted to an absolute asset specification by |
| | | combining the package supplied as ``package`` with the relative |
| | | resource specification supplied as ``renderer_name``. If you do |
| | | asset specification supplied as ``renderer_name``. If you do |
| | | not supply a ``package`` (or ``package`` is ``None``) the package |
| | | name of the *caller* of this function will be used as the package. |
| | | """ |
| | |
| | | isabs = os.path.isabs(name) |
| | | |
| | | if (not isabs) and (not ':' in name) and package: |
| | | # relative resource spec |
| | | # relative asset spec |
| | | if not isabs: |
| | | pp = package_path(package) |
| | | spec = os.path.join(pp, spec) |
| | | spec = resource_spec_from_abspath(spec, package) |
| | | spec = asset_spec_from_abspath(spec, package) |
| | | return spec |
| | | |
| | | @property # wait until completely necessary to look up translator |
| | |
| | | finally: |
| | | self.lock.release() |
| | | else: |
| | | # spec is a package:relpath resource spec |
| | | # spec is a package:relpath asset spec |
| | | renderer = registry.queryUtility(ITemplateRenderer, name=spec) |
| | | if renderer is None: |
| | | try: |
| | |
| | | filename) |
| | | if not pkg_resources.resource_exists(package_name, filename): |
| | | raise ValueError( |
| | | 'Missing template resource: %s (%s)' % (spec, abspath)) |
| | | 'Missing template asset: %s (%s)' % (spec, abspath)) |
| | | renderer = self.impl(abspath, self) |
| | | settings = info.settings or {} |
| | | if not settings.get('reload_resources'): |
| | | if not settings.get('reload_assets'): |
| | | # cache the template |
| | | self.lock.acquire() |
| | | try: |
| | |
| | | |
| | | 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 |
| | |
| | | """ |
| | | 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. |
| | | :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.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 |
| | | 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) |
| | | from pyramid.url import resource_url |
| | | resource_url(resource, request) |
| | | |
| | | .. note:: For backwards compatibility purposes, this method can also |
| | | be called as :meth:`pyramid.request.Request.model_url`. |
| | | """ |
| | | 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`. |
| | | The resource must live within a location defined via the |
| | | """ |
| | | Generates a fully qualified URL for a static :term:`asset`. The |
| | | asset must live within a location defined via the |
| | | :meth:`pyramid.config.Configurator.add_static_view` |
| | | :term:`configuration declaration` or the ``<static>`` ZCML |
| | | directive (see :ref:`static_resources_section`). |
| | | :term:`configuration declaration` or the ``<static>`` ZCML directive |
| | | (see :ref:`static_assets_section`). |
| | | |
| | | This is a convenience method. The result of calling |
| | | :meth:`pyramid.request.Request.static_url` is the same as calling |
| | |
| | | import os |
| | | import pkg_resources |
| | | |
| | | from zope.interface import implements |
| | | |
| | | from pyramid.interfaces import IPackageOverrides |
| | | |
| | | from pyramid.path import package_path |
| | | from pyramid.path import package_name |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | class OverrideProvider(pkg_resources.DefaultProvider): |
| | | def __init__(self, module): |
| | | pkg_resources.DefaultProvider.__init__(self, module) |
| | | self.module_name = module.__name__ |
| | | |
| | | def _get_overrides(self): |
| | | reg = get_current_registry() |
| | | overrides = reg.queryUtility(IPackageOverrides, self.module_name) |
| | | return overrides |
| | | |
| | | def get_resource_filename(self, manager, resource_name): |
| | | """ Return a true filesystem path for resource_name, |
| | | co-ordinating the extraction with manager, if the resource |
| | | must be unpacked to the filesystem. |
| | | """ |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | filename = overrides.get_filename(resource_name) |
| | | if filename is not None: |
| | | return filename |
| | | return pkg_resources.DefaultProvider.get_resource_filename( |
| | | self, manager, resource_name) |
| | | |
| | | def get_resource_stream(self, manager, resource_name): |
| | | """ Return a readable file-like object for resource_name.""" |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | stream = overrides.get_stream(resource_name) |
| | | if stream is not None: |
| | | return stream |
| | | return pkg_resources.DefaultProvider.get_resource_stream( |
| | | self, manager, resource_name) |
| | | |
| | | def get_resource_string(self, manager, resource_name): |
| | | """ Return a string containing the contents of resource_name.""" |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | string = overrides.get_string(resource_name) |
| | | if string is not None: |
| | | return string |
| | | return pkg_resources.DefaultProvider.get_resource_string( |
| | | self, manager, resource_name) |
| | | |
| | | def has_resource(self, resource_name): |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | result = overrides.has_resource(resource_name) |
| | | if result is not None: |
| | | return result |
| | | return pkg_resources.DefaultProvider.has_resource( |
| | | self, resource_name) |
| | | |
| | | def resource_isdir(self, resource_name): |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | result = overrides.isdir(resource_name) |
| | | if result is not None: |
| | | return result |
| | | return pkg_resources.DefaultProvider.resource_isdir( |
| | | self, resource_name) |
| | | |
| | | def resource_listdir(self, resource_name): |
| | | overrides = self._get_overrides() |
| | | if overrides is not None: |
| | | result = overrides.listdir(resource_name) |
| | | if result is not None: |
| | | return result |
| | | return pkg_resources.DefaultProvider.resource_listdir( |
| | | self, resource_name) |
| | | |
| | | class PackageOverrides: |
| | | implements(IPackageOverrides) |
| | | # pkg_resources arg in kw args below for testing |
| | | def __init__(self, package, pkg_resources=pkg_resources): |
| | | if hasattr(package, '__loader__') and not isinstance(package.__loader__, |
| | | self.__class__): |
| | | raise TypeError('Package %s already has a non-%s __loader__ ' |
| | | '(probably a module in a zipped egg)' % |
| | | (package, self.__class__)) |
| | | # We register ourselves as a __loader__ *only* to support the |
| | | # setuptools _find_adapter adapter lookup; this class doesn't |
| | | # actually support the PEP 302 loader "API". This is |
| | | # excusable due to the following statement in the spec: |
| | | # ... Loader objects are not |
| | | # required to offer any useful functionality (any such functionality, |
| | | # such as the zipimport get_data() method mentioned above, is |
| | | # optional)... |
| | | # A __loader__ attribute is basically metadata, and setuptools |
| | | # uses it as such. |
| | | package.__loader__ = self |
| | | # we call register_loader_type for every instantiation of this |
| | | # class; that's OK, it's idempotent to do it more than once. |
| | | pkg_resources.register_loader_type(self.__class__, OverrideProvider) |
| | | self.overrides = [] |
| | | self.overridden_package_name = package.__name__ |
| | | |
| | | def insert(self, path, package, prefix): |
| | | if not path or path.endswith('/'): |
| | | override = DirectoryOverride(path, package, prefix) |
| | | else: |
| | | override = FileOverride(path, package, prefix) |
| | | self.overrides.insert(0, override) |
| | | return override |
| | | |
| | | def search_path(self, resource_name): |
| | | for override in self.overrides: |
| | | o = override(resource_name) |
| | | if o is not None: |
| | | package, name = o |
| | | yield package, name |
| | | |
| | | def get_filename(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_filename(package, rname) |
| | | |
| | | def get_stream(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_stream(package, rname) |
| | | |
| | | def get_string(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_string(package, rname) |
| | | |
| | | def has_resource(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return True |
| | | |
| | | def isdir(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_isdir(package, rname) |
| | | |
| | | def listdir(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_listdir(package, rname) |
| | | |
| | | |
| | | class DirectoryOverride: |
| | | def __init__(self, path, package, prefix): |
| | | self.path = path |
| | | self.package = package |
| | | self.prefix = prefix |
| | | self.pathlen = len(self.path) |
| | | |
| | | def __call__(self, resource_name): |
| | | if resource_name.startswith(self.path): |
| | | name = '%s%s' % (self.prefix, resource_name[self.pathlen:]) |
| | | return self.package, name |
| | | |
| | | class FileOverride: |
| | | def __init__(self, path, package, prefix): |
| | | self.path = path |
| | | self.package = package |
| | | self.prefix = prefix |
| | | |
| | | def __call__(self, resource_name): |
| | | if resource_name == self.path: |
| | | return self.package, self.prefix |
| | | |
| | | def resolve_resource_spec(spec, pname='__main__'): |
| | | if pname and not isinstance(pname, basestring): |
| | | pname = pname.__name__ # as package |
| | | if os.path.isabs(spec): |
| | | return None, spec |
| | | filename = spec |
| | | if ':' in spec: |
| | | pname, filename = spec.split(':', 1) |
| | | elif pname is None: |
| | | pname, filename = None, spec |
| | | return pname, filename |
| | | |
| | | def resource_spec_from_abspath(abspath, package): |
| | | """ Try to convert an absolute path to a resource in a package to |
| | | a resource specification if possible; otherwise return the |
| | | absolute path. """ |
| | | if getattr(package, '__name__', None) == '__main__': |
| | | return abspath |
| | | pp = package_path(package) + os.path.sep |
| | | if abspath.startswith(pp): |
| | | relpath = abspath[len(pp):] |
| | | return '%s:%s' % (package_name(package), |
| | | relpath.replace(os.path.sep, '/')) |
| | | return abspath |
| | | |
| | | def abspath_from_resource_spec(spec, pname='__main__'): |
| | | if pname is None: |
| | | return spec |
| | | pname, filename = resolve_resource_spec(spec, pname) |
| | | if pname is None: |
| | | return filename |
| | | return pkg_resources.resource_filename(pname, filename) |
| | | """ Backwards compatibility shim module (forever). """ |
| | | from asset import * # b/w compat |
| | | resolve_resource_spec = resolve_asset_spec |
| | | resource_spec_from_abspath = asset_spec_from_abspath |
| | | abspath_from_resource_spec = abspath_from_asset_spec |
| | |
| | | from pyramid.request import Request |
| | | from pyramid.threadlocal import manager |
| | | from pyramid.traversal import DefaultRootFactory |
| | | from pyramid.traversal import ModelGraphTraverser |
| | | from pyramid.traversal import ResourceTreeTraverser |
| | | |
| | | from pyramid.config import Configurator # b/c |
| | | |
| | |
| | | # find a context |
| | | traverser = adapters.queryAdapter(root, ITraverser) |
| | | if traverser is None: |
| | | traverser = ModelGraphTraverser(root) |
| | | traverser = ResourceTreeTraverser(root) |
| | | tdict = traverser(request) |
| | | context, view_name, subpath, traversed, vroot, vroot_path =( |
| | | tdict['context'], tdict['view_name'], tdict['subpath'], |
| | |
| | | ``filename`` passed or the value in the ``options`` dictionary |
| | | named ``configure_zcml`` must be a) absolute pathname to a |
| | | :term:`ZCML` file that represents the application's configuration |
| | | *or* b) a :term:`resource specification` to a :term:`ZCML` file in |
| | | *or* b) a :term:`asset specification` to a :term:`ZCML` file in |
| | | the form ``dotted.package.name:relative/file/path.zcml``. |
| | | |
| | | ``filename`` is the filesystem path to a ZCML file (optionally |
| | | relative to the package path) that should be parsed to create the |
| | | application registry. It defaults to ``configure.zcml``. It can |
| | | also be a ;term:`resource specification` in the form |
| | | also be a ;term:`asset specification` in the form |
| | | ``dotted_package_name:relative/file/path.zcml``. Note that if any |
| | | value for ``configure_zcml`` is passed within the ``settings`` |
| | | dictionary, the value passed as ``filename`` will be ignored, |
| | |
| | | |
| | | def has_permission(permission, context, request): |
| | | """ Provided a permission (a string or unicode object), a context |
| | | (a :term:`model` instance) and a request object, return an |
| | | (a :term:`resource` instance) and a request object, return an |
| | | instance of :data:`pyramid.security.Allowed` if the permission |
| | | is granted in this context to the user implied by the |
| | | request. Return an instance of :mod:`pyramid.security.Denied` |
| | |
| | | return policy.effective_principals(request) |
| | | |
| | | def principals_allowed_by_permission(context, permission): |
| | | """ Provided a ``context`` (a model object), and a ``permission`` |
| | | """ Provided a ``context`` (a resource object), and a ``permission`` |
| | | (a string or unicode object), if a :term:`authorization policy` is |
| | | in effect, return a sequence of :term:`principal` ids that possess |
| | | the permission in the ``context``. If no authorization policy is |
| | |
| | | config_reload_templates = self.get('reload_templates', '') |
| | | eff_reload_templates = asbool(eget('BFG_RELOAD_TEMPLATES', |
| | | config_reload_templates)) |
| | | config_reload_assets = self.get('reload_assets', '') |
| | | config_reload_resources = self.get('reload_resources', '') |
| | | eff_reload_resources = asbool(eget('BFG_RELOAD_RESOURCES', |
| | | config_reload_resources)) |
| | | reload_assets = asbool(eget('BFG_RELOAD_ASSETS', |
| | | config_reload_assets)) |
| | | reload_resources = asbool(eget('BFG_RELOAD_RESOURCES', |
| | | config_reload_resources)) |
| | | # reload_resources is an older alias for reload_assets |
| | | eff_reload_assets = reload_assets or reload_resources |
| | | configure_zcml = self.get('configure_zcml', '') |
| | | eff_configure_zcml = eget('BFG_CONFIGURE_ZCML', configure_zcml) |
| | | locale_name = self.get('default_locale_name', 'en') |
| | |
| | | 'debug_routematch': eff_debug_all or eff_debug_routematch, |
| | | 'debug_templates': eff_debug_all or eff_debug_templates, |
| | | 'reload_templates': eff_reload_all or eff_reload_templates, |
| | | 'reload_resources':eff_reload_all or eff_reload_resources, |
| | | 'reload_resources':eff_reload_all or eff_reload_assets, |
| | | 'reload_assets':eff_reload_all or eff_reload_assets, |
| | | 'configure_zcml':eff_configure_zcml, |
| | | 'default_locale_name':eff_locale_name, |
| | | } |
| | |
| | | |
| | | from zope.interface import implements |
| | | |
| | | from pyramid.asset import resolve_asset_spec |
| | | from pyramid.interfaces import IStaticURLInfo |
| | | from pyramid.path import caller_package |
| | | from pyramid.resource import resolve_resource_spec |
| | | from pyramid.url import route_url |
| | | |
| | | class PackageURLParser(StaticURLParser): |
| | |
| | | |
| | | def add(self, name, spec, **extra): |
| | | # This feature only allows for the serving of a directory and |
| | | # the files contained within, not of a single resource; |
| | | # the files contained within, not of a single asset; |
| | | # appending a slash here if the spec doesn't have one is |
| | | # required for proper prefix matching done in ``generate`` |
| | | # (``subpath = path[len(spec):]``). |
| | |
| | | path into a response. |
| | | |
| | | You may pass an absolute or relative filesystem path or a |
| | | :term:`resource specification` representing the directory |
| | | :term:`asset specification` representing the directory |
| | | containing static files as the ``root_dir`` argument to this |
| | | class' constructor. |
| | | |
| | |
| | | five minutes). |
| | | |
| | | .. note:: If the ``root_dir`` is relative to a :term:`package`, or |
| | | is a :term:`resource specification` the :app:`Pyramid` |
| | | ``resource`` ZCML directive or |
| | | is a :term:`asset specification` the :app:`Pyramid` |
| | | ``asset`` ZCML directive or |
| | | :class:`pyramid.config.Configurator` method can be |
| | | used to override resources within the named ``root_dir`` |
| | | used to override assets within the named ``root_dir`` |
| | | package-relative directory. However, if the ``root_dir`` is |
| | | absolute, the ``resource`` directive will not be able to |
| | | override the resources it contains. """ |
| | | absolute, configuration will not be able to |
| | | override the assets it contains. """ |
| | | |
| | | def __init__(self, root_dir, cache_max_age=3600, package_name=None): |
| | | # package_name is for bw compat; it is preferred to pass in a |
| | |
| | | # (e.g. ``anotherpackage:foo/static``). |
| | | caller_package_name = caller_package().__name__ |
| | | package_name = package_name or caller_package_name |
| | | package_name, root_dir = resolve_resource_spec(root_dir, package_name) |
| | | package_name, root_dir = resolve_asset_spec(root_dir, package_name) |
| | | if package_name is None: |
| | | app = StaticURLParser(root_dir, cache_max_age=cache_max_age) |
| | | else: |
| | |
| | | config.commit() |
| | | return result |
| | | |
| | | def registerModels(models): |
| | | """ Registers a dictionary of :term:`model` objects that can be |
| | | resolved via the :func:`pyramid.traversal.find_model` API. |
| | | def registerResources(resources): |
| | | """ Registers a dictionary of :term:`resource` objects that can be |
| | | resolved via the :func:`pyramid.traversal.find_resource` API. |
| | | |
| | | The :func:`pyramid.traversal.find_model` API is called with a |
| | | The :func:`pyramid.traversal.find_resource` API is called with a |
| | | path as one of its arguments. If the dictionary you register when |
| | | calling this method contains that path as a string key |
| | | (e.g. ``/foo/bar`` or ``foo/bar``), the corresponding value will |
| | | be returned to ``find_model`` (and thus to your code) when |
| | | :func:`pyramid.traversal.find_model` is called with an |
| | | be returned to ``find_resource`` (and thus to your code) when |
| | | :func:`pyramid.traversal.find_resource` is called with an |
| | | equivalent path string or tuple. |
| | | |
| | | .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. |
| | | Instead use the |
| | | :meth:`pyramid.config.Configurator.testing_models` |
| | | :meth:`pyramid.config.Configurator.testing_resources` |
| | | method in your unit and integration tests. |
| | | |
| | | .. note:: For ancient backward compatibility purposes, this API can also |
| | | be accessed as :func:`pyramid.testing.registerModels`. |
| | | """ |
| | | registry = get_current_registry() |
| | | config = Configurator(registry=registry) |
| | | result = config.testing_models(models) |
| | | result = config.testing_resources(resources) |
| | | config.commit() |
| | | return result |
| | | |
| | | registerModels = registerResources |
| | | |
| | | def registerEventListener(event_iface=None): |
| | | """ Registers an :term:`event` listener (aka :term:`subscriber`) |
| | |
| | | v, k, myval)) |
| | | return True |
| | | |
| | | class DummyModel: |
| | | """ A dummy :app:`Pyramid` :term:`model` object.""" |
| | | class DummyResource: |
| | | """ A dummy :app:`Pyramid` :term:`resource` object.""" |
| | | def __init__(self, __name__=None, __parent__=None, __provides__=None, |
| | | **kw): |
| | | """ The model's ``__name__`` attribute will be set to the |
| | | value of the ``__name__`` argument, and the model's |
| | | """ The resource's ``__name__`` attribute will be set to the |
| | | value of the ``__name__`` argument, and the resource's |
| | | ``__parent__`` attribute will be set to the value of the |
| | | ``__parent__`` argument. If ``__provides__`` is specified, it |
| | | should be an interface object or tuple of interface objects |
| | | that will be attached to the resulting model via |
| | | that will be attached to the resulting resource via |
| | | :func:`zope.interface.alsoProvides`. Any extra keywords passed |
| | | in the ``kw`` argumnent will be set as direct attributes of |
| | | the model object.""" |
| | | the resource object.""" |
| | | self.__name__ = __name__ |
| | | self.__parent__ = __parent__ |
| | | if __provides__ is not None: |
| | |
| | | def __setitem__(self, name, val): |
| | | """ When the ``__setitem__`` method is called, the object |
| | | passed in as ``val`` will be decorated with a ``__parent__`` |
| | | attribute pointing at the dummy model and a ``__name__`` |
| | | attribute pointing at the dummy resource and a ``__name__`` |
| | | attribute that is the value of ``name``. The value will then |
| | | be returned when dummy model's ``__getitem__`` is called with |
| | | be returned when dummy resource's ``__getitem__`` is called with |
| | | the name ``name```.""" |
| | | val.__name__ = name |
| | | val.__parent__ = self |
| | |
| | | return name in self.subs |
| | | |
| | | def clone(self, __name__=_marker, __parent__=_marker, **kw): |
| | | """ Create a clone of the model object. If ``__name__`` or |
| | | """ Create a clone of the resource object. If ``__name__`` or |
| | | ``__parent__`` arguments are passed, use these values to |
| | | override the existing ``__name__`` or ``__parent__`` of the |
| | | model. If any extra keyword args are passed in via the ``kw`` |
| | | resource. If any extra keyword args are passed in via the ``kw`` |
| | | argument, use these keywords to add to or override existing |
| | | model keywords (attributes).""" |
| | | resource keywords (attributes).""" |
| | | oldkw = self.kw.copy() |
| | | oldkw.update(kw) |
| | | inst = self.__class__(self.__name__, self.__parent__, **oldkw) |
| | |
| | | if __parent__ is not _marker: |
| | | inst.__parent__ = __parent__ |
| | | return inst |
| | | |
| | | DummyModel = DummyResource # b/w compat (forever) |
| | | |
| | | class DummyRequest(object): |
| | | """ A dummy request object (imitates a :term:`request` object). |
| | |
| | | |
| | | Extra keyword arguments are assigned as attributes of the request |
| | | itself. |
| | | |
| | | .. note:: For backwards compatibility purposes, this class can also be |
| | | imported as :class:`pyramid.testing.DummyModel`. |
| | | """ |
| | | implements(IRequest) |
| | | method = 'GET' |
| | |
| | | which directly or indirectly uses: |
| | | |
| | | - any of the ``register*`` functions in :mod:`pyramid.testing` |
| | | (such as :func:`pyramid.testing.registerModels`) |
| | | (such as :func:`pyramid.testing.registerResources`) |
| | | |
| | | - any method of the :class:`pyramid.config.Configurator` |
| | | object returned by this function. |
File was renamed from pyramid/tests/test_resource.py |
| | |
| | | cleanUp() |
| | | |
| | | def _getTargetClass(self): |
| | | from pyramid.resource import OverrideProvider |
| | | from pyramid.asset import OverrideProvider |
| | | return OverrideProvider |
| | | |
| | | def _makeOne(self, module): |
| | |
| | | |
| | | def test_get_resource_filename_no_overrides(self): |
| | | import os |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | |
| | | |
| | | def test_get_resource_stream_no_overrides(self): |
| | | import os |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | |
| | | |
| | | def test_get_resource_string_no_overrides(self): |
| | | import os |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | |
| | | self.assertEqual(result, expected) |
| | | |
| | | def test_has_resource_no_overrides(self): |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | result = provider.has_resource(resource_name) |
| | | self.assertEqual(result, True) |
| | | |
| | | def test_resource_isdir_no_overrides(self): |
| | | file_resource_name = 'test_resource.py' |
| | | file_resource_name = 'test_asset.py' |
| | | directory_resource_name = 'fixtures' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | import os |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | import os |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | import os |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | |
| | | def test_has_resource_override_returns_None(self): |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | resource_name = 'test_resource.py' |
| | | resource_name = 'test_asset.py' |
| | | import pyramid.tests |
| | | provider = self._makeOne(pyramid.tests) |
| | | result = provider.has_resource(resource_name) |
| | |
| | | import pyramid.tests |
| | | self._registerOverrides(overrides) |
| | | provider = self._makeOne(pyramid.tests) |
| | | result = provider.get_resource_filename(None, 'test_resource.py') |
| | | result = provider.get_resource_filename(None, 'test_asset.py') |
| | | self.assertEqual(result, 'value') |
| | | |
| | | def test_get_resource_stream_override_returns_value(self): |
| | |
| | | import pyramid.tests |
| | | self._registerOverrides(overrides) |
| | | provider = self._makeOne(pyramid.tests) |
| | | result = provider.get_resource_stream(None, 'test_resource.py') |
| | | result = provider.get_resource_stream(None, 'test_asset.py') |
| | | self.assertEqual(result, 'value') |
| | | |
| | | def test_get_resource_string_override_returns_value(self): |
| | |
| | | import pyramid.tests |
| | | self._registerOverrides(overrides) |
| | | provider = self._makeOne(pyramid.tests) |
| | | result = provider.get_resource_string(None, 'test_resource.py') |
| | | result = provider.get_resource_string(None, 'test_asset.py') |
| | | self.assertEqual(result, 'value') |
| | | |
| | | def test_has_resource_override_returns_True(self): |
| | |
| | | import pyramid.tests |
| | | self._registerOverrides(overrides) |
| | | provider = self._makeOne(pyramid.tests) |
| | | result = provider.has_resource('test_resource.py') |
| | | result = provider.has_resource('test_asset.py') |
| | | self.assertEqual(result, True) |
| | | |
| | | def test_resource_isdir_override_returns_False(self): |
| | |
| | | |
| | | class TestPackageOverrides(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.resource import PackageOverrides |
| | | from pyramid.asset import PackageOverrides |
| | | return PackageOverrides |
| | | |
| | | def _makeOne(self, package, pkg_resources=None): |
| | |
| | | def test_get_filename(self): |
| | | import os |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests', 'test_resource.py'))] |
| | | ('pyramid.tests', 'test_asset.py'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | expected = os.path.join(here, 'test_resource.py') |
| | | expected = os.path.join(here, 'test_asset.py') |
| | | self.assertEqual(po.get_filename('whatever'), expected) |
| | | |
| | | def test_get_stream(self): |
| | | import os |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests', 'test_resource.py'))] |
| | | ('pyramid.tests', 'test_asset.py'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | expected = open(os.path.join(here, 'test_resource.py')).read() |
| | | expected = open(os.path.join(here, 'test_asset.py')).read() |
| | | self.assertEqual(po.get_stream('whatever').read(), expected) |
| | | |
| | | def test_get_string(self): |
| | | import os |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests', 'test_resource.py'))] |
| | | ('pyramid.tests', 'test_asset.py'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | expected = open(os.path.join(here, 'test_resource.py')).read() |
| | | expected = open(os.path.join(here, 'test_asset.py')).read() |
| | | self.assertEqual(po.get_string('whatever'), expected) |
| | | |
| | | def test_has_resource(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests', 'test_resource.py'))] |
| | | ('pyramid.tests', 'test_asset.py'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | |
| | | |
| | | def test_isdir_false(self): |
| | | overrides = [ DummyOverride( |
| | | ('pyramid.tests', 'test_resource.py'))] |
| | | ('pyramid.tests', 'test_asset.py'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | |
| | | |
| | | class TestDirectoryOverride(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.resource import DirectoryOverride |
| | | from pyramid.asset import DirectoryOverride |
| | | return DirectoryOverride |
| | | |
| | | def _makeOne(self, path, package, prefix): |
| | |
| | | result = o('baz/notfound.pt') |
| | | self.assertEqual(result, None) |
| | | |
| | | class Test_resolve_resource_spec(unittest.TestCase): |
| | | class Test_resolve_asset_spec(unittest.TestCase): |
| | | def _callFUT(self, spec, package_name='__main__'): |
| | | from pyramid.resource import resolve_resource_spec |
| | | return resolve_resource_spec(spec, package_name) |
| | | from pyramid.resource import resolve_asset_spec |
| | | return resolve_asset_spec(spec, package_name) |
| | | |
| | | def test_abspath(self): |
| | | import os |
| | |
| | | |
| | | def test_rel_spec(self): |
| | | pkg = 'pyramid.tests' |
| | | path = 'test_resource.py' |
| | | path = 'test_asset.py' |
| | | package_name, filename = self._callFUT(path, pkg) |
| | | self.assertEqual(package_name, 'pyramid.tests') |
| | | self.assertEqual(filename, 'test_resource.py') |
| | | self.assertEqual(filename, 'test_asset.py') |
| | | |
| | | def test_abs_spec(self): |
| | | pkg = 'pyramid.tests' |
| | | path = 'pyramid.nottests:test_resource.py' |
| | | path = 'pyramid.nottests:test_asset.py' |
| | | package_name, filename = self._callFUT(path, pkg) |
| | | self.assertEqual(package_name, 'pyramid.nottests') |
| | | self.assertEqual(filename, 'test_resource.py') |
| | | self.assertEqual(filename, 'test_asset.py') |
| | | |
| | | def test_package_name_is_None(self): |
| | | pkg = None |
| | | path = 'test_resource.py' |
| | | path = 'test_asset.py' |
| | | package_name, filename = self._callFUT(path, pkg) |
| | | self.assertEqual(package_name, None) |
| | | self.assertEqual(filename, 'test_resource.py') |
| | | self.assertEqual(filename, 'test_asset.py') |
| | | |
| | | def test_package_name_is_package_object(self): |
| | | import pyramid.tests |
| | | pkg = pyramid.tests |
| | | path = 'test_resource.py' |
| | | path = 'test_asset.py' |
| | | package_name, filename = self._callFUT(path, pkg) |
| | | self.assertEqual(package_name, 'pyramid.tests') |
| | | self.assertEqual(filename, 'test_resource.py') |
| | | self.assertEqual(filename, 'test_asset.py') |
| | | |
| | | |
| | | class TestFileOverride(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.resource import FileOverride |
| | | from pyramid.asset import FileOverride |
| | | return FileOverride |
| | | |
| | | def _makeOne(self, path, package, prefix): |
| | |
| | | result = o('notfound.pt') |
| | | self.assertEqual(result, None) |
| | | |
| | | class Test_abspath_from_resource_spec(unittest.TestCase): |
| | | class Test_abspath_from_asset_spec(unittest.TestCase): |
| | | def _callFUT(self, spec, pname='__main__'): |
| | | from pyramid.resource import abspath_from_resource_spec |
| | | return abspath_from_resource_spec(spec, pname) |
| | | from pyramid.resource import abspath_from_asset_spec |
| | | return abspath_from_asset_spec(spec, pname) |
| | | |
| | | def test_pname_is_None_before_resolve_resource_spec(self): |
| | | def test_pname_is_None_before_resolve_asset_spec(self): |
| | | result = self._callFUT('abc', None) |
| | | self.assertEqual(result, 'abc') |
| | | |
| | | def test_pname_is_None_after_resolve_resource_spec(self): |
| | | def test_pname_is_None_after_resolve_asset_spec(self): |
| | | result = self._callFUT('/abc', '__main__') |
| | | self.assertEqual(result, '/abc') |
| | | |
| | |
| | | self.assertEqual(result.ace, '<default deny>') |
| | | self.assertEqual( |
| | | result.acl, |
| | | '<No ACL found on any object in model lineage>') |
| | | '<No ACL found on any object in resource lineage>') |
| | | |
| | | def test_principals_allowed_by_permission_direct(self): |
| | | from pyramid.security import Allow |
| | |
| | | result = config.maybe_dotted(pyramid.tests) |
| | | self.assertEqual(result, pyramid.tests) |
| | | |
| | | def test_absolute_resource_spec_already_absolute(self): |
| | | def test_absolute_asset_spec_already_absolute(self): |
| | | import pyramid.tests |
| | | config = self._makeOne(package=pyramid.tests) |
| | | result = config.absolute_resource_spec('already:absolute') |
| | | result = config.absolute_asset_spec('already:absolute') |
| | | self.assertEqual(result, 'already:absolute') |
| | | |
| | | def test_absolute_resource_spec_notastring(self): |
| | | def test_absolute_asset_spec_notastring(self): |
| | | import pyramid.tests |
| | | config = self._makeOne(package=pyramid.tests) |
| | | result = config.absolute_resource_spec(None) |
| | | result = config.absolute_asset_spec(None) |
| | | self.assertEqual(result, None) |
| | | |
| | | def test_absolute_resource_spec_relative(self): |
| | | def test_absolute_asset_spec_relative(self): |
| | | import pyramid.tests |
| | | config = self._makeOne(package=pyramid.tests) |
| | | result = config.absolute_resource_spec('templates') |
| | | result = config.absolute_asset_spec('templates') |
| | | self.assertEqual(result, 'pyramid.tests:templates') |
| | | |
| | | def test_setup_registry_fixed(self): |
| | |
| | | config.add_translation_dirs, |
| | | '/wont/exist/on/my/system') |
| | | |
| | | def test_add_translation_dirs_resource_spec(self): |
| | | def test_add_translation_dirs_asset_spec(self): |
| | | import os |
| | | from pyramid.interfaces import ITranslationDirectories |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | inner_view, viewname='inner', wrapper_viewname='owrap') |
| | | self.assertRaises(ValueError, wrapped, None, request) |
| | | |
| | | def test_override_resource_samename(self): |
| | | def test_override_asset_samename(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') |
| | | self.assertRaises(ConfigurationError, config.override_asset,'a', 'a') |
| | | |
| | | def test_override_resource_directory_with_file(self): |
| | | def test_override_asset_directory_with_file(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_resource, |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo/', 'a:foo.pt') |
| | | |
| | | def test_override_resource_file_with_directory(self): |
| | | def test_override_asset_file_with_directory(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_resource, |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo.pt', 'a:foo/') |
| | | |
| | | def test_override_resource_success(self): |
| | | def test_override_asset_success(self): |
| | | config = self._makeOne(autocommit=True) |
| | | override = DummyUnderOverride() |
| | | config.override_resource( |
| | | config.override_asset( |
| | | 'pyramid.tests.fixtureapp:templates/foo.pt', |
| | | 'pyramid.tests.fixtureapp.subpackage:templates/bar.pt', |
| | | _override=override) |
| | |
| | | self.assertEqual(ut.groupids, ('group1', 'group2')) |
| | | self.assertEqual(ut.permissive, False) |
| | | |
| | | def test_testing_models(self): |
| | | from pyramid.traversal import find_model |
| | | def test_testing_resources(self): |
| | | from pyramid.traversal import find_resource |
| | | from pyramid.interfaces import ITraverser |
| | | ob1 = object() |
| | | ob2 = object() |
| | | models = {'/ob1':ob1, '/ob2':ob2} |
| | | resources = {'/ob1':ob1, '/ob2':ob2} |
| | | config = self._makeOne(autocommit=True) |
| | | config.testing_models(models) |
| | | config.testing_resources(resources) |
| | | adapter = config.registry.getAdapter(None, ITraverser) |
| | | result = adapter({'PATH_INFO':'/ob1'}) |
| | | self.assertEqual(result['context'], ob1) |
| | |
| | | self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) |
| | | try: |
| | | config.begin() |
| | | self.assertEqual(find_model(None, '/ob1'), ob1) |
| | | self.assertEqual(find_resource(None, '/ob1'), ob1) |
| | | finally: |
| | | config.end() |
| | | |
| | |
| | | os.path.join(module_path, 'a'), |
| | | os.path.join(module_path, 'b')]) |
| | | |
| | | def test_with_module_directory_resource_spec(self): |
| | | def test_with_module_directory_asset_spec(self): |
| | | import os |
| | | from pyramid.mako_templating import IMakoLookup |
| | | module_directory = 'pyramid.tests:fixtures' |
| | |
| | | fixtures = os.path.join(os.path.dirname(__file__), 'fixtures') |
| | | self.assertEqual(lookup.module_directory, fixtures) |
| | | |
| | | def test_with_module_directory_resource_abspath(self): |
| | | def test_with_module_directory_asset_abspath(self): |
| | | import os |
| | | from pyramid.mako_templating import IMakoLookup |
| | | fixtures = os.path.join(os.path.dirname(__file__), 'fixtures') |
| | |
| | | import pyramid.tests |
| | | return os.path.join(os.path.dirname(pyramid.tests.__file__), 'fixtures') |
| | | |
| | | def test_adjust_uri_not_resource_spec(self): |
| | | def test_adjust_uri_not_asset_spec(self): |
| | | inst = self._makeOne() |
| | | result = inst.adjust_uri('a', None) |
| | | self.assertEqual(result, '/a') |
| | | |
| | | def test_adjust_uri_resource_spec(self): |
| | | def test_adjust_uri_asset_spec(self): |
| | | inst = self._makeOne() |
| | | result = inst.adjust_uri('a:b', None) |
| | | self.assertEqual(result, 'a:b') |
| | | |
| | | def test_get_template_not_resource_spec(self): |
| | | def test_get_template_not_asset_spec(self): |
| | | fixturedir = self.get_fixturedir() |
| | | inst = self._makeOne(directories=[fixturedir]) |
| | | result = inst.get_template('helloworld.mak') |
| | | self.failIf(result is None) |
| | | |
| | | def test_get_template_resource_spec_with_filesystem_checks(self): |
| | | def test_get_template_asset_spec_with_filesystem_checks(self): |
| | | inst = self._makeOne(filesystem_checks=True) |
| | | result = inst.get_template('pyramid.tests:fixtures/helloworld.mak') |
| | | self.failIf(result is None) |
| | | |
| | | def test_get_template_resource_spec_missing(self): |
| | | def test_get_template_asset_spec_missing(self): |
| | | from mako.exceptions import TopLevelLookupException |
| | | fixturedir = self.get_fixturedir() |
| | | inst = self._makeOne(filesystem_checks=True, directories=[fixturedir]) |
| | |
| | | self.failUnless(factory.path.startswith(path)) |
| | | self.assertEqual(factory.kw, {}) |
| | | |
| | | def test_reload_resources_true(self): |
| | | def test_reload_assets_true(self): |
| | | import pyramid.tests |
| | | from pyramid.interfaces import ISettings |
| | | from pyramid.interfaces import ITemplateRenderer |
| | | settings = {'reload_resources':True} |
| | | settings = {'reload_assets':True} |
| | | testing.registerUtility(settings, ISettings) |
| | | renderer = {} |
| | | factory = DummyFactory(renderer) |
| | |
| | | self.assertEqual(reg.queryUtility(ITemplateRenderer, name=spec), |
| | | None) |
| | | |
| | | def test_reload_resources_false(self): |
| | | def test_reload_assets_false(self): |
| | | import pyramid.tests |
| | | from pyramid.interfaces import ITemplateRenderer |
| | | settings = {'reload_resources':False} |
| | | settings = {'reload_assets':False} |
| | | renderer = {} |
| | | factory = DummyFactory(renderer) |
| | | spec = 'test_renderers.py' |
| | |
| | | self.assertEqual(inst.called2, True) |
| | | self.assertEqual(inst.finished_callbacks, []) |
| | | |
| | | def test_model_url(self): |
| | | def test_resource_url(self): |
| | | self._registerContextURL() |
| | | inst = self._makeOne({}) |
| | | root = DummyContext() |
| | | result = inst.model_url(root) |
| | | result = inst.resource_url(root) |
| | | self.assertEqual(result, 'http://example.com/context/') |
| | | |
| | | def test_route_url(self): |
| | |
| | | self.assertEqual(result['reload_templates'], True) |
| | | |
| | | def test_reload_resources(self): |
| | | # alias for reload_assets |
| | | result = self._makeOne({}) |
| | | self.assertEqual(result['reload_resources'], False) |
| | | self.assertEqual(result['reload_assets'], False) |
| | | result = self._makeOne({'reload_resources':'false'}) |
| | | self.assertEqual(result['reload_resources'], False) |
| | | self.assertEqual(result['reload_assets'], False) |
| | | result = self._makeOne({'reload_resources':'t'}) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | result = self._makeOne({'reload_resources':'1'}) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | result = self._makeOne({}, {'BFG_RELOAD_RESOURCES':'1'}) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | result = self._makeOne({'reload_resources':'false'}, |
| | | {'BFG_RELOAD_RESOURCES':'1'}) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | |
| | | def test_reload_assets(self): |
| | | # alias for reload_resources |
| | | result = self._makeOne({}) |
| | | self.assertEqual(result['reload_assets'], False) |
| | | self.assertEqual(result['reload_resources'], False) |
| | | result = self._makeOne({'reload_assets':'false'}) |
| | | self.assertEqual(result['reload_resources'], False) |
| | | self.assertEqual(result['reload_assets'], False) |
| | | result = self._makeOne({'reload_assets':'t'}) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | result = self._makeOne({'reload_assets':'1'}) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | result = self._makeOne({}, {'BFG_RELOAD_ASSETS':'1'}) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | result = self._makeOne({'reload_assets':'false'}, |
| | | {'BFG_RELOAD_ASSETS':'1'}) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | |
| | | |
| | | def test_reload_all(self): |
| | | result = self._makeOne({}) |
| | | self.assertEqual(result['reload_templates'], False) |
| | | self.assertEqual(result['reload_resources'], False) |
| | | self.assertEqual(result['reload_assets'], False) |
| | | result = self._makeOne({'reload_all':'false'}) |
| | | self.assertEqual(result['reload_templates'], False) |
| | | self.assertEqual(result['reload_resources'], False) |
| | | self.assertEqual(result['reload_assets'], False) |
| | | result = self._makeOne({'reload_all':'t'}) |
| | | self.assertEqual(result['reload_templates'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | result = self._makeOne({'reload_all':'1'}) |
| | | self.assertEqual(result['reload_templates'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | result = self._makeOne({}, {'BFG_RELOAD_ALL':'1'}) |
| | | self.assertEqual(result['reload_templates'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | result = self._makeOne({'reload_all':'false'}, |
| | | {'BFG_RELOAD_ALL':'1'}) |
| | | self.assertEqual(result['reload_templates'], True) |
| | | self.assertEqual(result['reload_resources'], True) |
| | | self.assertEqual(result['reload_assets'], True) |
| | | |
| | | def test_debug_authorization(self): |
| | | result = self._makeOne({}) |
| | |
| | | self.assertEqual(ut.groupids, ('group1', 'group2')) |
| | | self.assertEqual(ut.permissive, False) |
| | | |
| | | class Test_registerModels(TestBase): |
| | | def test_registerModels(self): |
| | | class Test_registerResources(TestBase): |
| | | def test_it(self): |
| | | ob1 = object() |
| | | ob2 = object() |
| | | models = {'/ob1':ob1, '/ob2':ob2} |
| | | resources = {'/ob1':ob1, '/ob2':ob2} |
| | | from pyramid import testing |
| | | testing.registerModels(models) |
| | | testing.registerResources(resources) |
| | | from pyramid.interfaces import ITraverser |
| | | adapter = self.registry.getAdapter(None, ITraverser) |
| | | result = adapter({'PATH_INFO':'/ob1'}) |
| | |
| | | self.assertEqual(result['virtual_root'], ob2) |
| | | self.assertEqual(result['virtual_root_path'], ()) |
| | | self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) |
| | | from pyramid.traversal import find_model |
| | | self.assertEqual(find_model(None, '/ob1'), ob1) |
| | | from pyramid.traversal import find_resource |
| | | self.assertEqual(find_resource(None, '/ob1'), ob1) |
| | | |
| | | class Test_registerTemplateRenderer(TestBase): |
| | | def test_registerTemplateRenderer(self): |
| | |
| | | |
| | | |
| | | |
| | | class TestDummyModel(unittest.TestCase): |
| | | class TestDummyResource(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.testing import DummyModel |
| | | return DummyModel |
| | | from pyramid.testing import DummyResource |
| | | return DummyResource |
| | | |
| | | def _makeOne(self, name=None, parent=None, **kw): |
| | | klass = self._getTargetClass() |
| | |
| | | class Dummy: |
| | | pass |
| | | dummy = Dummy() |
| | | model = self._makeOne() |
| | | model['abc'] = dummy |
| | | resource = self._makeOne() |
| | | resource['abc'] = dummy |
| | | self.assertEqual(dummy.__name__, 'abc') |
| | | self.assertEqual(dummy.__parent__, model) |
| | | self.assertEqual(model['abc'], dummy) |
| | | self.assertEqual(model.get('abc'), dummy) |
| | | self.assertRaises(KeyError, model.__getitem__, 'none') |
| | | self.failUnless('abc' in model) |
| | | del model['abc'] |
| | | self.failIf('abc' in model) |
| | | self.assertEqual(model.get('abc', 'foo'), 'foo') |
| | | self.assertEqual(model.get('abc'), None) |
| | | self.assertEqual(dummy.__parent__, resource) |
| | | self.assertEqual(resource['abc'], dummy) |
| | | self.assertEqual(resource.get('abc'), dummy) |
| | | self.assertRaises(KeyError, resource.__getitem__, 'none') |
| | | self.failUnless('abc' in resource) |
| | | del resource['abc'] |
| | | self.failIf('abc' in resource) |
| | | self.assertEqual(resource.get('abc', 'foo'), 'foo') |
| | | self.assertEqual(resource.get('abc'), None) |
| | | |
| | | def test_extra_params(self): |
| | | model = self._makeOne(foo=1) |
| | | self.assertEqual(model.foo, 1) |
| | | resource = self._makeOne(foo=1) |
| | | self.assertEqual(resource.foo, 1) |
| | | |
| | | def test_clone(self): |
| | | model = self._makeOne('name', 'parent', foo=1, bar=2) |
| | | clone = model.clone('name2', 'parent2', bar=1) |
| | | resource = self._makeOne('name', 'parent', foo=1, bar=2) |
| | | clone = resource.clone('name2', 'parent2', bar=1) |
| | | self.assertEqual(clone.bar, 1) |
| | | self.assertEqual(clone.__name__, 'name2') |
| | | self.assertEqual(clone.__parent__, 'parent2') |
| | |
| | | def test_keys_items_values_len(self): |
| | | class Dummy: |
| | | pass |
| | | model = self._makeOne() |
| | | model['abc'] = Dummy() |
| | | model['def'] = Dummy() |
| | | self.assertEqual(model.values(), model.subs.values()) |
| | | self.assertEqual(model.items(), model.subs.items()) |
| | | self.assertEqual(model.keys(), model.subs.keys()) |
| | | self.assertEqual(len(model), 2) |
| | | resource = self._makeOne() |
| | | resource['abc'] = Dummy() |
| | | resource['def'] = Dummy() |
| | | self.assertEqual(resource.values(), resource.subs.values()) |
| | | self.assertEqual(resource.items(), resource.subs.items()) |
| | | self.assertEqual(resource.keys(), resource.subs.keys()) |
| | | self.assertEqual(len(resource), 2) |
| | | |
| | | def test_nonzero(self): |
| | | model = self._makeOne() |
| | | self.assertEqual(model.__nonzero__(), True) |
| | | resource = self._makeOne() |
| | | self.assertEqual(resource.__nonzero__(), True) |
| | | |
| | | def test_ctor_with__provides__(self): |
| | | model = self._makeOne(__provides__=IDummy) |
| | | self.failUnless(IDummy.providedBy(model)) |
| | | resource = self._makeOne(__provides__=IDummy) |
| | | self.failUnless(IDummy.providedBy(resource)) |
| | | |
| | | class TestDummyRequest(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | |
| | | path = unicode('/La Pe\xc3\xb1a', 'utf-8') |
| | | self.assertRaises(UnicodeEncodeError, self._callFUT, path) |
| | | |
| | | class ModelGraphTraverserTests(unittest.TestCase): |
| | | class ResourceTreeTraverserTests(unittest.TestCase): |
| | | def setUp(self): |
| | | cleanUp() |
| | | |
| | |
| | | cleanUp() |
| | | |
| | | def _getTargetClass(self): |
| | | from pyramid.traversal import ModelGraphTraverser |
| | | return ModelGraphTraverser |
| | | from pyramid.traversal import ResourceTreeTraverser |
| | | return ResourceTreeTraverser |
| | | |
| | | def _makeOne(self, *arg, **kw): |
| | | klass = self._getTargetClass() |
| | |
| | | self.assertRaises(URLDecodeError, policy, environ) |
| | | |
| | | def test_withroute_nothingfancy(self): |
| | | model = DummyContext() |
| | | traverser = self._makeOne(model) |
| | | resource = DummyContext() |
| | | traverser = self._makeOne(resource) |
| | | environ = {'bfg.routes.matchdict': {}} |
| | | result = traverser(environ) |
| | | self.assertEqual(result['context'], model) |
| | | self.assertEqual(result['context'], resource) |
| | | self.assertEqual(result['view_name'], '') |
| | | self.assertEqual(result['subpath'], ()) |
| | | self.assertEqual(result['traversed'], ()) |
| | | self.assertEqual(result['root'], model) |
| | | self.assertEqual(result['virtual_root'], model) |
| | | self.assertEqual(result['root'], resource) |
| | | self.assertEqual(result['virtual_root'], resource) |
| | | self.assertEqual(result['virtual_root_path'], ()) |
| | | |
| | | def test_withroute_with_subpath_string(self): |
| | | model = DummyContext() |
| | | traverser = self._makeOne(model) |
| | | resource = DummyContext() |
| | | traverser = self._makeOne(resource) |
| | | environ = {'bfg.routes.matchdict': {'subpath':'/a/b/c'}} |
| | | result = traverser(environ) |
| | | self.assertEqual(result['context'], model) |
| | | self.assertEqual(result['context'], resource) |
| | | self.assertEqual(result['view_name'], '') |
| | | self.assertEqual(result['subpath'], ('a', 'b','c')) |
| | | self.assertEqual(result['traversed'], ()) |
| | | self.assertEqual(result['root'], model) |
| | | self.assertEqual(result['virtual_root'], model) |
| | | self.assertEqual(result['root'], resource) |
| | | self.assertEqual(result['virtual_root'], resource) |
| | | self.assertEqual(result['virtual_root_path'], ()) |
| | | |
| | | def test_withroute_with_subpath_tuple(self): |
| | | model = DummyContext() |
| | | traverser = self._makeOne(model) |
| | | resource = DummyContext() |
| | | traverser = self._makeOne(resource) |
| | | environ = {'bfg.routes.matchdict': {'subpath':('a', 'b', 'c')}} |
| | | result = traverser(environ) |
| | | self.assertEqual(result['context'], model) |
| | | self.assertEqual(result['context'], resource) |
| | | self.assertEqual(result['view_name'], '') |
| | | self.assertEqual(result['subpath'], ('a', 'b','c')) |
| | | self.assertEqual(result['traversed'], ()) |
| | | self.assertEqual(result['root'], model) |
| | | self.assertEqual(result['virtual_root'], model) |
| | | self.assertEqual(result['root'], resource) |
| | | self.assertEqual(result['virtual_root'], resource) |
| | | self.assertEqual(result['virtual_root_path'], ()) |
| | | |
| | | def test_withroute_and_traverse_string(self): |
| | | model = DummyContext() |
| | | traverser = self._makeOne(model) |
| | | resource = DummyContext() |
| | | traverser = self._makeOne(resource) |
| | | environ = {'bfg.routes.matchdict': {'traverse':'foo/bar'}} |
| | | result = traverser(environ) |
| | | self.assertEqual(result['context'], model) |
| | | self.assertEqual(result['context'], resource) |
| | | self.assertEqual(result['view_name'], 'foo') |
| | | self.assertEqual(result['subpath'], ('bar',)) |
| | | self.assertEqual(result['traversed'], ()) |
| | | self.assertEqual(result['root'], model) |
| | | self.assertEqual(result['virtual_root'], model) |
| | | self.assertEqual(result['root'], resource) |
| | | self.assertEqual(result['virtual_root'], resource) |
| | | self.assertEqual(result['virtual_root_path'], ()) |
| | | |
| | | def test_withroute_and_traverse_tuple(self): |
| | | model = DummyContext() |
| | | traverser = self._makeOne(model) |
| | | resource = DummyContext() |
| | | traverser = self._makeOne(resource) |
| | | environ = {'bfg.routes.matchdict': {'traverse':('foo', 'bar')}} |
| | | result = traverser(environ) |
| | | self.assertEqual(result['context'], model) |
| | | self.assertEqual(result['context'], resource) |
| | | self.assertEqual(result['view_name'], 'foo') |
| | | self.assertEqual(result['subpath'], ('bar',)) |
| | | self.assertEqual(result['traversed'], ()) |
| | | self.assertEqual(result['root'], model) |
| | | self.assertEqual(result['virtual_root'], model) |
| | | self.assertEqual(result['root'], resource) |
| | | self.assertEqual(result['virtual_root'], resource) |
| | | self.assertEqual(result['virtual_root_path'], ()) |
| | | |
| | | class FindInterfaceTests(unittest.TestCase): |
| | |
| | | result = self._callFUT(baz) |
| | | self.assertEqual(result, dummy) |
| | | |
| | | class FindModelTests(unittest.TestCase): |
| | | class FindResourceTests(unittest.TestCase): |
| | | def _callFUT(self, context, name): |
| | | from pyramid.traversal import find_model |
| | | return find_model(context, name) |
| | | from pyramid.traversal import find_resource |
| | | return find_resource(context, name) |
| | | |
| | | def _registerTraverser(self, traverser): |
| | | from pyramid.threadlocal import get_current_registry |
| | |
| | | reg.registerAdapter(traverser, (Interface,), ITraverser) |
| | | |
| | | def test_list(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, ['']) |
| | | self.assertEqual(result, model) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '/') |
| | | result = self._callFUT(resource, ['']) |
| | | self.assertEqual(result, resource) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_generator(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | def foo(): |
| | | yield '' |
| | | result = self._callFUT(model, foo()) |
| | | self.assertEqual(result, model) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '/') |
| | | result = self._callFUT(resource, foo()) |
| | | self.assertEqual(result, resource) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_self_string_found(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, '') |
| | | self.assertEqual(result, model) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '') |
| | | result = self._callFUT(resource, '') |
| | | self.assertEqual(result, resource) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '') |
| | | |
| | | def test_self_tuple_found(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, ()) |
| | | self.assertEqual(result, model) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '') |
| | | result = self._callFUT(resource, ()) |
| | | self.assertEqual(result, resource) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '') |
| | | |
| | | def test_relative_string_found(self): |
| | | model = DummyContext() |
| | | resource = DummyContext() |
| | | baz = DummyContext() |
| | | traverser = make_traverser({'context':baz, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, 'baz') |
| | | result = self._callFUT(resource, 'baz') |
| | | self.assertEqual(result, baz) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], 'baz') |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') |
| | | |
| | | def test_relative_tuple_found(self): |
| | | model = DummyContext() |
| | | resource = DummyContext() |
| | | baz = DummyContext() |
| | | traverser = make_traverser({'context':baz, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, ('baz',)) |
| | | result = self._callFUT(resource, ('baz',)) |
| | | self.assertEqual(result, baz) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], 'baz') |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') |
| | | |
| | | def test_relative_string_notfound(self): |
| | | model = DummyContext() |
| | | resource = DummyContext() |
| | | baz = DummyContext() |
| | | traverser = make_traverser({'context':baz, 'view_name':'bar'}) |
| | | self._registerTraverser(traverser) |
| | | self.assertRaises(KeyError, self._callFUT, model, 'baz') |
| | | self.assertEqual(model.request.environ['PATH_INFO'], 'baz') |
| | | self.assertRaises(KeyError, self._callFUT, resource, 'baz') |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') |
| | | |
| | | def test_relative_tuple_notfound(self): |
| | | model = DummyContext() |
| | | resource = DummyContext() |
| | | baz = DummyContext() |
| | | traverser = make_traverser({'context':baz, 'view_name':'bar'}) |
| | | self._registerTraverser(traverser) |
| | | self.assertRaises(KeyError, self._callFUT, model, ('baz',)) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], 'baz') |
| | | self.assertRaises(KeyError, self._callFUT, resource, ('baz',)) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') |
| | | |
| | | def test_absolute_string_found(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, '/') |
| | | result = self._callFUT(resource, '/') |
| | | self.assertEqual(result, root) |
| | | self.assertEqual(root.wascontext, True) |
| | | self.assertEqual(root.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_absolute_tuple_found(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(model, ('',)) |
| | | result = self._callFUT(resource, ('',)) |
| | | self.assertEqual(result, root) |
| | | self.assertEqual(root.wascontext, True) |
| | | self.assertEqual(root.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_absolute_string_notfound(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':'fuz'}) |
| | | self._registerTraverser(traverser) |
| | | self.assertRaises(KeyError, self._callFUT, model, '/') |
| | | self.assertRaises(KeyError, self._callFUT, resource, '/') |
| | | self.assertEqual(root.wascontext, True) |
| | | self.assertEqual(root.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_absolute_tuple_notfound(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':'fuz'}) |
| | | self._registerTraverser(traverser) |
| | | self.assertRaises(KeyError, self._callFUT, model, ('',)) |
| | | self.assertRaises(KeyError, self._callFUT, resource, ('',)) |
| | | self.assertEqual(root.wascontext, True) |
| | | self.assertEqual(root.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_absolute_unicode_found(self): |
| | | # test for bug wiggy found in wild, traceback stack: |
| | | # root = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF' |
| | | # wiggy's code: section=find_model(page, root) |
| | | # find_model L76: D = traverse(model, path) |
| | | # wiggy's code: section=find_resource(page, root) |
| | | # find_resource L76: D = traverse(resource, path) |
| | | # traverse L291: return traverser(request) |
| | | # __call__ line 568: vpath_tuple = traversal_path(vpath) |
| | | # lru_cached line 91: f(*arg) |
| | |
| | | # |
| | | # solution: encode string to ascii in pyramid.traversal.traverse |
| | | # before passing it along to webob as path_info |
| | | from pyramid.traversal import ModelGraphTraverser |
| | | from pyramid.traversal import ResourceTreeTraverser |
| | | unprintable = DummyContext() |
| | | root = DummyContext(unprintable) |
| | | unprintable.__parent__ = root |
| | |
| | | '/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8') |
| | | root.__parent__ = None |
| | | root.__name__ = None |
| | | traverser = ModelGraphTraverser |
| | | traverser = ResourceTreeTraverser |
| | | self._registerTraverser(traverser) |
| | | result = self._callFUT(root, u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF') |
| | | self.assertEqual(result, unprintable) |
| | | |
| | | class ModelPathTests(unittest.TestCase): |
| | | def _callFUT(self, model, *elements): |
| | | from pyramid.traversal import model_path |
| | | return model_path(model, *elements) |
| | | class ResourcePathTests(unittest.TestCase): |
| | | def _callFUT(self, resource, *elements): |
| | | from pyramid.traversal import resource_path |
| | | return resource_path(resource, *elements) |
| | | |
| | | def test_it(self): |
| | | baz = DummyContext() |
| | |
| | | result = self._callFUT(other2) |
| | | self.assertEqual(result, '//other2') |
| | | |
| | | class ModelPathTupleTests(unittest.TestCase): |
| | | def _callFUT(self, model, *elements): |
| | | from pyramid.traversal import model_path_tuple |
| | | return model_path_tuple(model, *elements) |
| | | class ResourcePathTupleTests(unittest.TestCase): |
| | | def _callFUT(self, resource, *elements): |
| | | from pyramid.traversal import resource_path_tuple |
| | | return resource_path_tuple(resource, *elements) |
| | | |
| | | def test_it(self): |
| | | baz = DummyContext() |
| | |
| | | result = context_url() |
| | | self.assertEqual(result, 'http://example.com:5432/') |
| | | |
| | | def test_call_unicode_mixed_with_bytes_in_model_names(self): |
| | | def test_call_unicode_mixed_with_bytes_in_resource_names(self): |
| | | root = DummyContext() |
| | | root.__parent__ = None |
| | | root.__name__ = None |
| | |
| | | def tearDown(self): |
| | | cleanUp() |
| | | |
| | | def _callFUT(self, model, request): |
| | | def _callFUT(self, resource, request): |
| | | from pyramid.traversal import virtual_root |
| | | return virtual_root(model, request) |
| | | return virtual_root(resource, request) |
| | | |
| | | def test_registered(self): |
| | | from pyramid.interfaces import IContextURL |
| | |
| | | |
| | | def test_request_has_registry(self): |
| | | from pyramid.threadlocal import get_current_registry |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, ['']) |
| | | self.assertEqual(model.request.registry, get_current_registry()) |
| | | self._callFUT(resource, ['']) |
| | | self.assertEqual(resource.request.registry, get_current_registry()) |
| | | |
| | | def test_list(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, ['']) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '/') |
| | | self._callFUT(resource, ['']) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_generator(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | def foo(): |
| | | yield '' |
| | | self._callFUT(model, foo()) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '/') |
| | | self._callFUT(resource, foo()) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_self_string_found(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, '') |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '') |
| | | self._callFUT(resource, '') |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '') |
| | | |
| | | def test_self_tuple_found(self): |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, ()) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '') |
| | | self._callFUT(resource, ()) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '') |
| | | |
| | | def test_relative_string_found(self): |
| | | model = DummyContext() |
| | | resource = DummyContext() |
| | | baz = DummyContext() |
| | | traverser = make_traverser({'context':baz, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, 'baz') |
| | | self.assertEqual(model.request.environ['PATH_INFO'], 'baz') |
| | | self._callFUT(resource, 'baz') |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') |
| | | |
| | | def test_relative_tuple_found(self): |
| | | model = DummyContext() |
| | | resource = DummyContext() |
| | | baz = DummyContext() |
| | | traverser = make_traverser({'context':baz, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, ('baz',)) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], 'baz') |
| | | self._callFUT(resource, ('baz',)) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') |
| | | |
| | | def test_absolute_string_found(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, '/') |
| | | self._callFUT(resource, '/') |
| | | self.assertEqual(root.wascontext, True) |
| | | self.assertEqual(root.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_absolute_tuple_found(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, ('',)) |
| | | self._callFUT(resource, ('',)) |
| | | self.assertEqual(root.wascontext, True) |
| | | self.assertEqual(root.request.environ['PATH_INFO'], '/') |
| | | |
| | | def test_empty_sequence(self): |
| | | root = DummyContext() |
| | | model = DummyContext() |
| | | model.__parent__ = root |
| | | model.__name__ = 'baz' |
| | | resource = DummyContext() |
| | | resource.__parent__ = root |
| | | resource.__name__ = 'baz' |
| | | traverser = make_traverser({'context':root, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, []) |
| | | self.assertEqual(model.wascontext, True) |
| | | self.assertEqual(model.request.environ['PATH_INFO'], '') |
| | | self._callFUT(resource, []) |
| | | self.assertEqual(resource.wascontext, True) |
| | | self.assertEqual(resource.request.environ['PATH_INFO'], '') |
| | | |
| | | def test_default_traverser(self): |
| | | model = DummyContext() |
| | | result = self._callFUT(model, '') |
| | | resource = DummyContext() |
| | | result = self._callFUT(resource, '') |
| | | self.assertEqual(result['view_name'], '') |
| | | self.assertEqual(result['context'], model) |
| | | self.assertEqual(result['context'], resource) |
| | | |
| | | def test_requestfactory_overridden(self): |
| | | from pyramid.interfaces import IRequestFactory |
| | |
| | | class MyRequest(Request): |
| | | pass |
| | | reg.registerUtility(MyRequest, IRequestFactory) |
| | | model = DummyContext() |
| | | traverser = make_traverser({'context':model, 'view_name':''}) |
| | | resource = DummyContext() |
| | | traverser = make_traverser({'context':resource, 'view_name':''}) |
| | | self._registerTraverser(traverser) |
| | | self._callFUT(model, ['']) |
| | | self.assertEqual(model.request.__class__, MyRequest) |
| | | self._callFUT(resource, ['']) |
| | | self.assertEqual(resource.request.__class__, MyRequest) |
| | | |
| | | class TestDefaultRootFactory(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | |
| | | |
| | | from pyramid.testing import cleanUp |
| | | |
| | | class ModelURLTests(unittest.TestCase): |
| | | class ResourceURLTests(unittest.TestCase): |
| | | def setUp(self): |
| | | cleanUp() |
| | | |
| | | def tearDown(self): |
| | | cleanUp() |
| | | |
| | | def _callFUT(self, model, request, *elements, **kw): |
| | | from pyramid.url import model_url |
| | | return model_url(model, request, *elements, **kw) |
| | | def _callFUT(self, resource, request, *elements, **kw): |
| | | from pyramid.url import resource_url |
| | | return resource_url(resource, request, *elements, **kw) |
| | | |
| | | def _registerContextURL(self, reg): |
| | | from pyramid.interfaces import IContextURL |
| | |
| | | request = DummyRequest() |
| | | self.assertRaises(Forbidden, view, None, request) |
| | | |
| | | class TestResourceDirective(unittest.TestCase): |
| | | class TestAssetDirective(unittest.TestCase): |
| | | def setUp(self): |
| | | self.config = testing.setUp(autocommit=False) |
| | | self.config._ctx = self.config._make_context() |
| | |
| | | testing.tearDown() |
| | | |
| | | def _callFUT(self, *arg, **kw): |
| | | from pyramid.zcml import resource |
| | | return resource(*arg, **kw) |
| | | from pyramid.zcml import asset |
| | | return asset(*arg, **kw) |
| | | |
| | | def test_it(self): |
| | | import pyramid.tests |
| | |
| | | result = self._callFUT(context, '/foo.pt') |
| | | self.assertEqual(result, '/foo.pt') |
| | | |
| | | def test_path_is_already_resource_spec(self): |
| | | def test_path_is_already_asset_spec(self): |
| | | context = DummyContext() |
| | | result = self._callFUT(context, 'pyramid.tests:foo.pt') |
| | | self.assertEqual(result, 'pyramid.tests:foo.pt') |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | 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``. |
| | | |
| | | .. note:: For backwards compatibility purposes, this function can also |
| | | be imported as :func:`pyramid.traversal.find_model`. |
| | | """ |
| | | 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). |
| | | 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). |
| | | |
| | | Model path strings returned will be escaped in the following |
| | | 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. |
| | | |
| | | .. note:: For backwards compatibility purposes, this function can also |
| | | be imported as ``model_path``. |
| | | """ |
| | | # 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)) |
| | | |
| | | def traverse(model, path): |
| | | """Given a model object as ``model`` and a string or tuple |
| | | model_path = resource_path # b/w compat |
| | | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | 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. |
| | | |
| | |
| | | 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. |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | path = path.encode('ascii') |
| | | |
| | | if path and path[0] == '/': |
| | | model = find_root(model) |
| | | resource = find_root(resource) |
| | | |
| | | reg = get_current_registry() |
| | | |
| | |
| | | |
| | | 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 |
| | |
| | | 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)) |
| | | |
| | | 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)] |
| | | .. note:: For backwards compatibility purposes, this function can also |
| | | be imported as ``model_path_tuple``. |
| | | |
| | | """ |
| | | return tuple(_resource_path_list(resource, *elements)) |
| | | |
| | | 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, |
| | |
| | | :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) |
| | |
| | | (u'archives', u'<unprintable unicode>') |
| | | |
| | | .. 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 |
| | |
| | | |
| | | 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 |
| | |
| | | _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) |
| | |
| | | '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 |
| | |
| | | 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: |
| | |
| | | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | |
| | | 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. |
| | |
| | | |
| | | 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 |
| | |
| | | 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). |
| | | |
| | | .. note:: For backwards compatibility purposes, this function can also |
| | | be imported as ``model_url``. |
| | | """ |
| | | try: |
| | | reg = request.registry |
| | | 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 = '' |
| | |
| | | else: |
| | | suffix = '' |
| | | |
| | | return model_url + suffix + qs + anchor |
| | | return resource_url + suffix + qs + anchor |
| | | |
| | | model_url = resource_url # b/w compat (forever) |
| | | |
| | | def static_url(path, request, **kw): |
| | | """ |
| | | Generates a fully qualified URL for a static :term:`resource`. |
| | | The resource must live within a location defined via the |
| | | Generates a fully qualified URL for a static :term:`asset`. |
| | | The asset must live within a location defined via the |
| | | :meth:`pyramid.config.Configurator.add_static_view` |
| | | :term:`configuration declaration` or the ``<static>`` ZCML |
| | | directive (see :ref:`static_resources_section`). |
| | | directive (see :ref:`static_assets_section`). |
| | | |
| | | .. note:: Calling :meth:`pyramid.Request.static_url` can be used to |
| | | achieve the same result as :func:`pyramid.url.static_url`. |
| | |
| | | |
| | | The ``path`` argument points at a file or directory on disk which |
| | | a URL should be generated for. The ``path`` may be either a |
| | | relative path (e.g. ``static/foo.css``) or a :term:`resource |
| | | relative path (e.g. ``static/foo.css``) or a :term:`asset |
| | | specification` (e.g. ``mypackage:static/foo.css``). A ``path`` |
| | | may not be an absolute filesystem path (a :exc:`ValueError` will |
| | | be raised if this function is supplied with an absolute path). |
| | |
| | | the :func:`pyramid.url.route_url` ``**kw`` argument. See the |
| | | documentation for that function to understand the arguments which |
| | | you can provide to it. However, typically, you don't need to pass |
| | | anything as ``*kw`` when generating a static resource URL. |
| | | anything as ``*kw`` when generating a static asset URL. |
| | | |
| | | This function raises a :exc:`ValueError` if a static view |
| | | definition cannot be found which matches the path specification. |
| | |
| | | """ |
| | | if os.path.isabs(path): |
| | | raise ValueError('Absolute paths cannot be used to generate static ' |
| | | 'urls (use a package-relative path or a resource ' |
| | | 'urls (use a package-relative path or an asset ' |
| | | 'specification).') |
| | | if not ':' in path: |
| | | # if it's not a package:relative/name and it's not an |
| | |
| | | |
| | | For example, this code in a module ``views.py``:: |
| | | |
| | | from models import MyModel |
| | | from resources import MyResource |
| | | |
| | | @view_config(name='my_view', context=MyModel, permission='read', |
| | | @view_config(name='my_view', context=MyResource, permission='read', |
| | | route_name='site1') |
| | | def my_view(context, request): |
| | | return 'OK' |
| | |
| | | :meth:`pyramid.config.Configurator.add_view` method:: |
| | | |
| | | import views |
| | | import models |
| | | config.add_view(views.my_view, context=models.MyModel, name='my_view', |
| | | from resources import MyResource |
| | | config.add_view(views.my_view, context=MyResource, name='my_view', |
| | | permission='read', 'route_name='site1') |
| | | |
| | | Or might replace the following ZCML ``view`` declaration:: |
| | | |
| | | <view |
| | | for='.models.MyModel' |
| | | for='.resources.MyResource' |
| | | view='.views.my_view' |
| | | name='my_view' |
| | | permission='read' |
| | |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | from pyramid.config import Configurator |
| | | from pyramid.exceptions import ConfigurationError |
| | | from pyramid.resource import resource_spec_from_abspath |
| | | from pyramid.asset import asset_spec_from_abspath |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | ###################### directives ########################## |
| | |
| | | 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) |
| | | |
| | | class IRepozeWho1AuthenticationPolicyDirective(Interface): |
| | | identifier_name = TextLine(title=u'identitfier_name', required=False, |
| | |
| | | required=True) |
| | | |
| | | path = TextLine( |
| | | title=u'Path to the directory which contains resources', |
| | | title=u'Path to the directory which contains assets', |
| | | description=u'May be package-relative by using a colon to ' |
| | | 'separate package name and path relative to the package directory.', |
| | | required=True) |
| | |
| | | config.set_default_permission(name) |
| | | |
| | | def path_spec(context, path): |
| | | # we prefer registering resource specifications over absolute |
| | | # paths because these can be overridden by the resource directive. |
| | | # we prefer registering asset specifications over absolute |
| | | # paths because these can be overridden by the asset directive. |
| | | if ':' in path and not os.path.isabs(path): |
| | | # it's already a resource specification |
| | | # it's already an asset specification |
| | | return path |
| | | abspath = context.path(path) |
| | | if hasattr(context, 'package') and context.package: |
| | | return resource_spec_from_abspath(abspath, context.package) |
| | | return asset_spec_from_abspath(abspath, context.package) |
| | | return abspath |
| | | |
| | | def zcml_configure(name, package): |