diff options
| author | Chris McDonough <chrism@plope.com> | 2010-12-18 15:53:49 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2010-12-18 15:53:49 -0500 |
| commit | 738a2b5f3eb44da7036a31005144e5252827ac38 (patch) | |
| tree | 80e10d2ae215b2e0e1f248354b6c9a5e11406cd5 | |
| parent | aa6c4267b330a5665dcb7c98d75fe21f8eeececb (diff) | |
| parent | 70119302324e5bf5627344f90c62ef31b6e43005 (diff) | |
| download | pyramid-738a2b5f3eb44da7036a31005144e5252827ac38.tar.gz pyramid-738a2b5f3eb44da7036a31005144e5252827ac38.tar.bz2 pyramid-738a2b5f3eb44da7036a31005144e5252827ac38.zip | |
Merge branch 'model2resource'
Conflicts:
docs/narr/views.rst
95 files changed, 2378 insertions, 2287 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 2f8a784c0..bdb7acd97 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,77 @@ 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 --------- @@ -30,6 +101,9 @@ Documentation - 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 ---------------- diff --git a/docs/api/config.rst b/docs/api/config.rst index 64dc4d0dd..fd8e1042d 100644 --- a/docs/api/config.rst +++ b/docs/api/config.rst @@ -32,7 +32,7 @@ .. 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) @@ -58,7 +58,7 @@ .. automethod:: make_wsgi_app() - .. automethod:: override_resource(to_override, override_with) + .. automethod:: override_asset(to_override, override_with) .. automethod:: scan(package=None, categories=None) @@ -78,7 +78,7 @@ .. automethod:: testing_securitypolicy - .. automethod:: testing_models + .. automethod:: testing_resources .. automethod:: testing_add_subscriber diff --git a/docs/api/request.rst b/docs/api/request.rst index acd66ccf8..13b434a51 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -26,7 +26,7 @@ .. 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. diff --git a/docs/api/testing.rst b/docs/api/testing.rst index fc4b76cc7..fdcdadee8 100644 --- a/docs/api/testing.rst +++ b/docs/api/testing.rst @@ -7,7 +7,7 @@ .. autofunction:: registerDummySecurityPolicy - .. autofunction:: registerModels + .. autofunction:: registerResources .. autofunction:: registerEventListener @@ -31,7 +31,7 @@ .. autofunction:: cleanUp - .. autoclass:: DummyModel + .. autoclass:: DummyResource :members: .. autoclass:: DummyRequest diff --git a/docs/api/traversal.rst b/docs/api/traversal.rst index b89691847..cd4765e04 100644 --- a/docs/api/traversal.rst +++ b/docs/api/traversal.rst @@ -7,13 +7,13 @@ .. 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 diff --git a/docs/api/url.rst b/docs/api/url.rst index 8c702a3fb..1aa3082b7 100644 --- a/docs/api/url.rst +++ b/docs/api/url.rst @@ -5,7 +5,7 @@ .. 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 diff --git a/docs/designdefense.rst b/docs/designdefense.rst index 1a8868883..5aa149824 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -606,77 +606,48 @@ folks that haven't yet spent that 30 minutes. 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. - -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`. - -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). - -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. - -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. +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 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 +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), 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 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 -------------------------------------------------------- @@ -705,7 +676,7 @@ top of your object graph (or any administrative interface), you can register 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 @@ -745,7 +716,7 @@ which match information in an associated "urlconf" such as 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 @@ -993,14 +964,16 @@ frameworks have some sort of event system hooked up that lets the view 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: @@ -1151,9 +1124,9 @@ access. I like this, because it means: 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 diff --git a/docs/glossary.rst b/docs/glossary.rst index 2494a6cdf..a27d73766 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -38,20 +38,20 @@ Glossary 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`. @@ -120,10 +120,10 @@ Glossary 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 @@ -131,25 +131,33 @@ Glossary 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 @@ -159,7 +167,7 @@ Glossary :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 @@ -167,19 +175,19 @@ Glossary 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. @@ -189,20 +197,20 @@ Glossary 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 @@ -225,9 +233,9 @@ Glossary 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. @@ -416,7 +424,7 @@ Glossary 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 @@ -476,7 +484,7 @@ Glossary :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. diff --git a/docs/index.rst b/docs/index.rst index 073a562ca..3add95117 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -48,7 +48,7 @@ Narrative documentation in chapter form explaining how to use narr/webob narr/sessions narr/templates - narr/models + narr/resources narr/security narr/i18n narr/vhosting @@ -58,7 +58,7 @@ Narrative documentation in chapter form explaining how to use narr/hooks narr/declarative narr/extending - narr/resources + narr/assets narr/router narr/threadlocals narr/zca diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 0ef67248b..09e449fe3 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -41,7 +41,7 @@ Narrative Documentation narr/webob narr/sessions narr/templates - narr/models + narr/resources narr/security narr/i18n narr/vhosting @@ -51,7 +51,7 @@ Narrative Documentation narr/hooks narr/declarative narr/extending - narr/resources + narr/assets narr/router narr/startup narr/threadlocals @@ -119,6 +119,7 @@ ZCML Directive Reference zcml/aclauthorizationpolicy zcml/adapter zcml/authtktauthenticationpolicy + zcml/asset zcml/configure zcml/default_permission zcml/forbidden @@ -127,7 +128,6 @@ ZCML Directive Reference zcml/remoteuserauthenticationpolicy zcml/renderer zcml/repozewho1authenticationpolicy - zcml/resource zcml/route zcml/scan zcml/static diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py index 05730a115..813028704 100644 --- a/docs/narr/MyProject/myproject/__init__.py +++ b/docs/narr/MyProject/myproject/__init__.py @@ -1,12 +1,12 @@ 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() diff --git a/docs/narr/MyProject/myproject/models.py b/docs/narr/MyProject/myproject/models.py deleted file mode 100644 index 75dec7505..000000000 --- a/docs/narr/MyProject/myproject/models.py +++ /dev/null @@ -1,7 +0,0 @@ -class MyModel(object): - pass - -root = MyModel() - -def get_root(request): - return root diff --git a/docs/narr/MyProject/myproject/resources.py b/docs/narr/MyProject/myproject/resources.py new file mode 100644 index 000000000..3d811895c --- /dev/null +++ b/docs/narr/MyProject/myproject/resources.py @@ -0,0 +1,3 @@ +class Root(object): + def __init__(self, request): + self.request = request diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst new file mode 100644 index 000000000..1932e19ff --- /dev/null +++ b/docs/narr/assets.rst @@ -0,0 +1,198 @@ +.. index:: + single: assets + +.. _assets_chapter: + +Assets +====== + +An :term:`asset` is any file contained within a Python :term:`package` which +is *not* a Python source code file. For example, each of the following is an +asset: + +- a :term:`Chameleon` template file contained within a Python package. + +- a GIF image file contained within a Python package. + +- a CSS file contained within a Python package. + +- a JavaScript source file contained within a Python package. + +- A directory within a package that does not have an ``__init__.py`` + in it (if it possessed an ``__init__.py`` it would *be* a package). + +The use of assets is quite common in most web development projects. For +example, when you create a :app:`Pyramid` application using one of the +available "paster" templates, as described in :ref:`creating_a_project`, the +directory representing the application contains a Python :term:`package`. +Within that Python package, there are directories full of files which are +assets. For example, there is a ``templates`` directory which contains +``.pt`` files, and a ``static`` directory which contains ``.css``, ``.js``, +and ``.gif`` files. + +.. _understanding_assets: + +Understanding Assets +-------------------- + +Let's imagine you've created a :app:`Pyramid` application that uses a +:term:`Chameleon` ZPT template via the +:func:`pyramid.chameleon_zpt.render_template_to_response` API. For example, +the application might address the asset named ``templates/some_template.pt`` +using that API within a ``views.py`` file inside a ``myapp`` package: + +.. ignore-next-block +.. code-block:: python + :linenos: + + from pyramid.chameleon_zpt import render_template_to_response + render_template_to_response('templates/some_template.pt') + +"Under the hood", when this API is called, :app:`Pyramid` attempts +to make sense out of the string ``templates/some_template.pt`` +provided by the developer. To do so, it first finds the "current" +package. The "current" package is the Python package in which the +``views.py`` module which contains this code lives. This would be the +``myapp`` package, according to our example so far. By resolving the +current package, :app:`Pyramid` has enough information to locate +the actual template file. These are the elements it needs: + +- The *package name* (``myapp``) + +- The *asset name* (``templates/some_template.pt``) + +:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the package name +and asset name to an absolute (operating-system-specific) file name. It +eventually passes this resolved absolute filesystem path to the Chameleon +templating engine, which then uses it to load, parse, and execute the +template file. + +Package names often contain dots. For example, ``pyramid`` is a package. +Asset names usually look a lot like relative UNIX file paths. + +.. index:: + pair: overriding; assets + +.. _overriding_assets_section: + +Overriding Assets +----------------- + +It can often be useful to override specific assets from "outside" a given +:app:`Pyramid` application. For example, you may wish to reuse an existing +:app:`Pyramid` application more or less unchanged. However, some specific +template file owned by the application might have inappropriate HTML, or some +static asset (such as a logo file or some CSS file) might not be appropriate. +You *could* just fork the application entirely, but it's often more +convenient to just override the assets that are inappropriate and reuse the +application "as is". This is particularly true when you reuse some "core" +application over and over again for some set of customers (such as a CMS +application, or some bug tracking application), and you want to make +arbitrary visual modifications to a particular application deployment without +forking the underlying code. + +To this end, :app:`Pyramid` contains a feature that makes it possible to +"override" one asset with one or more other assets. In support of this +feature, a :term:`Configurator` API exists named +:meth:`pyramid.config.Configurator.override_asset`. This API allows you to +*override* the following kinds of assets defined in any Python package: + +- Individual :term:`Chameleon` templates. + +- A directory containing multiple Chameleon templates. + +- Individual static files served up by an instance of the + ``pyramid.view.static`` helper class. + +- A directory of static files served up by an instance of the + ``pyramid.view.static`` helper class. + +- Any other asset (or set of assets) addressed by code that uses the + setuptools :term:`pkg_resources` API. + +.. note:: The :term:`ZCML` directive named ``asset`` serves the same purpose + as the :meth:`pyramid.config.Configurator.override_asset` method. + +.. index:: + single: override_asset + +.. _override_asset: + +The ``override_asset`` API +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An individual call to :meth:`pyramid.config.Configurator.override_asset` +can override a single asset. For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset( + to_override='some.package:templates/mytemplate.pt', + override_with='another.package:othertemplates/anothertemplate.pt') + +The string value passed to both ``to_override`` and ``override_with`` sent to +the ``override_asset`` API is called an :term:`asset specification`. The +colon separator in a specification separates the *package name* from the +*asset name*. The colon and the following asset name are optional. If they +are not specified, the override attempts to resolve every lookup into a +package from the directory of another package. For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='some.package', + override_with='another.package') + +Individual subdirectories within a package can also be overridden: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='some.package:templates/', + override_with='another.package:othertemplates/') + + +If you wish to override a directory with another directory, you *must* +make sure to attach the slash to the end of both the ``to_override`` +specification and the ``override_with`` specification. If you fail to +attach a slash to the end of a specification that points to a directory, +you will get unexpected results. + +You cannot override a directory specification with a file specification, and +vice versa: a startup error will occur if you try. You cannot override an +asset with itself: a startup error will occur if you try. + +Only individual *package* assets may be overridden. Overrides will not +traverse through subpackages within an overridden package. This means that +if you want to override assets for both ``some.package:templates``, and +``some.package.views:templates``, you will need to register two overrides. + +The package name in a specification may start with a dot, meaning that +the package is relative to the package in which the configuration +construction file resides (or the ``package`` argument to the +:class:`pyramid.config.Configurator` class construction). +For example: + +.. ignore-next-block +.. code-block:: python + :linenos: + + config.override_asset(to_override='.subpackage:templates/', + override_with='another.package:templates/') + +Multiple calls to ``override_asset`` which name a shared ``to_override`` but +a different ``override_with`` specification can be "stacked" to form a search +path. The first asset that exists in the search path will be used; if no +asset exists in the override path, the original asset is used. + +Asset overrides can actually override assets other than templates and static +files. Any software which uses the +:func:`pkg_resources.get_resource_filename`, +:func:`pkg_resources.get_resource_stream` or +:func:`pkg_resources.get_resource_string` APIs will obtain an overridden file +when an override is used. + diff --git a/docs/narr/contextfinding.rst b/docs/narr/contextfinding.rst index 691ad7b8e..ad0d1f9da 100644 --- a/docs/narr/contextfinding.rst +++ b/docs/narr/contextfinding.rst @@ -95,20 +95,20 @@ don't deal very well with URLs that represent arbitrary-depth hierarchies. But :term:`traversal` *does* work well for URLs that represent -arbitrary-depth hierarchies. Since the path segments that compose a -URL are addressed separately, it becomes very easy to form URLs that -represent arbitrary depth hierarchies in a system that uses traversal. -When you're willing to treat your application models as a graph that -can be traversed, it also becomes easy to provide "instance-level -security": you just attach a security declaration to each instance in -the graph. This is not nearly as easy to do when using URL dispatch. +arbitrary-depth hierarchies. Since the path segments that compose a URL are +addressed separately, it becomes very easy to form URLs that represent +arbitrary depth hierarchies in a system that uses traversal. When you're +willing to treat your application resources as a tree that can be traversed, +it also becomes easy to provide "instance-level security": you just attach a +security declaration to each instance in the tree. This is not nearly as +easy to do when using URL dispatch. In essence, the choice to use traversal vs. URL dispatch is largely religious. Traversal dispatch probably just doesn't make any sense when you possess completely "square" data stored in a relational database because it requires the construction and maintenance of a -graph and requires that the developer think about mapping URLs to code -in terms of traversing that graph. However, when you have a +tree and requires that the developer think about mapping URLs to code +in terms of traversing that tree. However, when you have a hierarchical data store, using traversal can provide significant advantages over using URL-based dispatch. diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst index 99c5a75ac..28dec4ff1 100644 --- a/docs/narr/declarative.rst +++ b/docs/narr/declarative.rst @@ -557,7 +557,7 @@ example of a view declaration in ZCML is as follows: :linenos: <view - context=".models.Hello" + context=".resources.Hello" view=".views.hello_world" name="hello.html" /> @@ -566,7 +566,7 @@ The above maps the ``.views.hello_world`` view callable function to the following set of :term:`context finding` results: - A :term:`context` object which is an instance (or subclass) of the - Python class represented by ``.models.Hello`` + Python class represented by ``.resources.Hello`` - A :term:`view name` equalling ``hello.html``. @@ -575,28 +575,27 @@ the following set of :term:`context finding` results: above) mean "relative to the Python package directory in which this :term:`ZCML` file is stored". So if the above ``view`` declaration was made inside a ``configure.zcml`` file that lived in the - ``hello`` package, you could replace the relative ``.models.Hello`` - with the absolute ``hello.models.Hello``; likewise you could + ``hello`` package, you could replace the relative ``.resources.Hello`` + with the absolute ``hello.resources.Hello``; likewise you could replace the relative ``.views.hello_world`` with the absolute ``hello.views.hello_world``. Either the relative or absolute form is functionally equivalent. It's often useful to use the relative form, in case your package's name changes. It's also shorter to type. -You can also declare a *default view callable* for a :term:`model` -type: +You can also declare a *default view callable* for a :term:`resource` type: .. code-block:: xml :linenos: <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``. @@ -608,7 +607,7 @@ string as its ``name`` attribute: :linenos: <view - context=".models.Hello" + context=".resources.Hello" view=".views.hello_world" name="" /> @@ -721,19 +720,19 @@ documentation. .. 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 @@ -752,7 +751,7 @@ absolute path. 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: @@ -775,7 +774,7 @@ package-relative path. 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 @@ -792,7 +791,7 @@ While the ``path`` argument can be a number of different things, the ``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`. @@ -820,9 +819,78 @@ detail later in this chapter. 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: Enabling an Authorization Policy Via ZCML @@ -833,12 +901,11 @@ than imperative configuration, modify the ZCML file loaded by your application (usually named ``configure.zcml``) to enable an authorization policy. -For example, to enable a policy which compares the value of an "auth -ticket" cookie passed in the request's environment which contains a -reference to a single :term:`principal` against the principals present -in any :term:`ACL` found in model data when attempting to call some -:term:`view`, modify your ``configure.zcml`` to look something like -this: +For example, to enable a policy which compares the value of an "auth ticket" +cookie passed in the request's environment which contains a reference to a +single :term:`principal` against the principals present in any :term:`ACL` +found in the resource tree when attempting to call some :term:`view`, modify +your ``configure.zcml`` to look something like this: .. code-block:: xml :linenos: @@ -958,7 +1025,7 @@ Built-In Authorization Policy ZCML Directives ``aclauthorizationpolicy`` When this directive is used, authorization information is obtained -from :term:`ACL` objects attached to model instances. +from :term:`ACL` objects attached to resources. An example of its usage, with all attributes fully expanded: @@ -1069,7 +1136,6 @@ with ``.jinja2`` as its ``renderer`` value. The ``name`` passed to the See also :ref:`renderer_directive` and :meth:`pyramid.config.Configurator.add_renderer`. - Overriding an Existing Renderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1207,4 +1273,3 @@ See also :ref:`subscriber_directive` and :ref:`events_chapter`. .. - hooks chapter still has topics for ZCML -.. - resources chapter still has topics for ZCML diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst index 08fa7af44..0c6f3dd03 100644 --- a/docs/narr/environment.rst +++ b/docs/narr/environment.rst @@ -3,7 +3,7 @@ single: settings single: reload single: debug_authorization - single: reload_resources + single: reload_assets single: debug_notfound single: debug_all single: reload_all @@ -52,21 +52,25 @@ template rendering extensions. | | | +---------------------------------+-----------------------------+ -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 ----------------------- @@ -174,7 +178,7 @@ Mako Directories ++++++++++++++++ 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``. +-----------------------------+ @@ -333,40 +337,38 @@ affect settings that do not start with ``reload_*`` such as .. index:: single: reload_templates - single: reload_resources - -Understanding the Distinction Between ``reload_templates`` and ``reload_resources`` ------------------------------------------------------------------------------------ - -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. - -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). - -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. + single: reload_assets + +Understanding the Distinction Between ``reload_templates`` and ``reload_assets`` +-------------------------------------------------------------------------------- + +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_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_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. diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 1f9fd1288..9802a01f6 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -191,15 +191,15 @@ the original application with slight tweaks. For example: :linenos: <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 diff --git a/docs/narr/handlers.rst b/docs/narr/handlers.rst index f6e658cf0..5b6f71984 100644 --- a/docs/narr/handlers.rst +++ b/docs/narr/handlers.rst @@ -100,7 +100,7 @@ Using :meth:`~pyramid.config.Configurator.add_handler` 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`. @@ -112,7 +112,7 @@ For example: 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. diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 10f463a17..006f5d5cb 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -88,11 +88,10 @@ callable: it is false. .. warning:: When a NotFound view callable accepts an argument list as - described in :ref:`request_and_context_view_definitions`, the - ``context`` passed as the first argument to the view callable will - be the :exc:`pyramid.exceptions.NotFound` exception instance. - If available, the *model* context will still be available as - ``request.context``. + described in :ref:`request_and_context_view_definitions`, the ``context`` + passed as the first argument to the view callable will be the + :exc:`pyramid.exceptions.NotFound` exception instance. If available, the + resource context will still be available as ``request.context``. .. index:: single: forbidden view @@ -221,14 +220,14 @@ a class that implements the following interface: """ Return a dictionary with (at least) the keys ``root``, ``context``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. These values are - typically the result of an object graph traversal. ``root`` - is the physical root object, ``context`` will be a model + typically the result of a resource tree traversal. ``root`` + is the physical root object, ``context`` will be a resource object, ``view_name`` will be the view name used (a Unicode name), ``subpath`` will be a sequence of Unicode names that followed the view name but were not traversed, ``traversed`` will be a sequence of Unicode names that were traversed (including the virtual root path, if any) ``virtual_root`` - will be a model object representing the virtual root (or the + will be a resource object representing the virtual root (or the physical root if traversal was not performed), and ``virtual_root_path`` will be a sequence representing the virtual root path (a sequence of Unicode names) or None if @@ -255,30 +254,29 @@ traverser would be used. For example: <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``: @@ -288,13 +286,13 @@ of :term:`context` by adding an adapter stanza for <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). diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index da77a28f0..f9dab9ff6 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -171,7 +171,7 @@ match is straightforward. When a route is matched: Root factories related to a route were explained previously within :ref:`route_factories`. Both the global root factory and default root factory were explained previously within - :ref:`the_object_graph`. + :ref:`the_resource_tree`. .. _using_traverse_in_a_route_pattern: @@ -226,7 +226,7 @@ we've created a root factory that looks like so in a module named .. code-block:: python :linenos: - class Traversable(object): + class Resource(object): def __init__(self, subobjects): self.subobjects = subobjects @@ -234,15 +234,15 @@ we've created a root factory that looks like so in a module named return self.subobjects[name] root = Traversable( - {'a':Traversable({'b':Traversable({'c':Traversable({})})})} + {'a':Resource({'b':Resource({'c':Resource({})})})} ) def root_factory(request): return root -Above, we've defined a (bogus) graph that can be traversed, and a -``root_factory`` function that can be used as part of a particular -route configuration statement: +Above, we've defined a (bogus) resource tree that can be traversed, and a +``root_factory`` function that can be used as part of a particular route +configuration statement: .. code-block:: python :linenos: @@ -250,14 +250,13 @@ route configuration statement: config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') -The ``factory`` above points at the function we've defined. It will -return an instance of the ``Traversable`` class as a root object -whenever this route is matched. Instances of the``Traversable`` class -can be used for graph traversal because they have a ``__getitem__`` -method that does something nominally useful. Since traversal uses -``__getitem__`` to walk the nodes of an object graph, using traversal -against the root object implied by our route statement is a reasonable -thing to do. +The ``factory`` above points at the function we've defined. It will return +an instance of the ``Traversable`` class as a root object whenever this route +is matched. Instances of the``Resource`` class can be used for tree +traversal because they have a ``__getitem__`` method that does something +nominally useful. Since traversal uses ``__getitem__`` to walk the resources +of a resource tree, using traversal against the root resource implied by our +route statement is a reasonable thing to do. .. note:: @@ -271,12 +270,11 @@ thing to do. When the route configuration named ``home`` above is matched during a request, the matchdict generated will be based on its pattern: -``{foo}/{bar}/*traverse``. The "capture value" implied by the -``*traverse`` element in the pattern will be used to traverse the -graph in order to find a context, starting from the root object -returned from the root factory. In the above example, the -:term:`root` object found will be the instance named ``root`` in -``routes.py``. +``{foo}/{bar}/*traverse``. The "capture value" implied by the ``*traverse`` +element in the pattern will be used to traverse the resource tree in order to +find a context, starting from the root object returned from the root factory. +In the above example, the :term:`root` object found will be the instance +named ``root`` in ``routes.py``. If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``, is ``http://example.com/one/two/a/b/c``, the traversal path used @@ -284,12 +282,11 @@ against the root object will be ``a/b/c``. As a result, :app:`Pyramid` will attempt to traverse through the edges ``a``, ``b``, and ``c``, beginning at the root object. -In our above example, this particular set of traversal steps will mean -that the :term:`context` of the view would be the ``Traversable`` -object we've named ``c`` in our bogus graph and the :term:`view name` -resulting from traversal will be the empty string; if you need a -refresher about why this outcome is presumed, see -:ref:`traversal_algorithm`. +In our above example, this particular set of traversal steps will mean that +the :term:`context` of the view would be the ``Traversable`` object we've +named ``c`` in our bogus resource tree and the :term:`view name` resulting +from traversal will be the empty string; if you need a refresher about why +this outcome is presumed, see :ref:`traversal_algorithm`. At this point, a suitable view callable will be found and invoked using :term:`view lookup` as described in :ref:`view_configuration`, @@ -347,7 +344,7 @@ above ``mypackage.views.another_view`` view will be invoked when: - the :term:`context` is any object. For instance, if the URL ``http://example.com/one/two/a/another`` is provided -to an application that uses the previously mentioned object graph, the +to an application that uses the previously mentioned resource tree, the ``mypackage.views.another`` view callable will be called instead of the ``mypackage.views.myview`` view callable because the :term:`view name` will be ``another`` instead of the empty string. diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 7c725690d..3ade3726c 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -144,10 +144,9 @@ it only supplies a mechanism to map URLs to :term:`view` code, along with a set of conventions for calling those views. You are free to use third-party components that fit your needs in your applications. -The concepts of :term:`view` and :term:`model` are used by -:app:`Pyramid` mostly as they would be by Django. -:app:`Pyramid` has a documentation culture more like Django's than -like Zope's. +The concept of :term:`view` is used by :app:`Pyramid` mostly as it would be +by Django. :app:`Pyramid` has a documentation culture more like Django's +than like Zope's. Like :term:`Pylons` version 1.0, but unlike :term:`Zope`, a :app:`Pyramid` application developer may use completely imperative @@ -177,13 +176,13 @@ frameworks, :app:`Pyramid` also generally fits into this class. .. sidebar:: You Say :app:`Pyramid` is MVC, But Where's The Controller? - The :app:`Pyramid` authors believe that the MVC pattern just - doesn't really fit the web very well. In a :app:`Pyramid` - application, there are models, which store data, and views, which - present the data stored in models. However, no facility provided - by the framework actually maps to the concept of a "controller". - So :app:`Pyramid` is actually an "MV" framework rather than an - "MVC" framework. "MVC", however, is close enough as a general - classification moniker for purposes of comparison with other web - frameworks. - + The :app:`Pyramid` authors believe that the MVC pattern just doesn't + really fit the web very well. In a :app:`Pyramid` application, there is a + 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. diff --git a/docs/narr/models.rst b/docs/narr/models.rst deleted file mode 100644 index 91828287f..000000000 --- a/docs/narr/models.rst +++ /dev/null @@ -1,331 +0,0 @@ -Models -====== - -A :term:`model` class is typically a simple Python class defined in a module. -References to these classes and instances of such classes are omnipresent in -:app:`Pyramid` when :term:`traversal` is used to build an application: - -- Model instances make up the object graph that :app:`Pyramid` - will walk over when :term:`traversal` is used. - -- The ``context`` and ``containment`` arguments to - :meth:`pyramid.config.Configurator.add_view` often - reference a model class. - -- A :term:`root factory` returns a model instance. - -- A model instance is exposed to :term:`view` code as the :term:`context` of - a view. - -In many :term:`traversal` based applications (particularly ones that use -Zope-like patterns), model objects often store data and offer methods related -to mutating that data. - -.. note:: - - A terminology overlap confuses people who write applications that - always use ORM packages such as SQLAlchemy, which has a very - different notion of the definition of a "model". When using the API - of common ORM packages, its conception of "model" is almost - certainly not the same conception of "model" used by - :app:`Pyramid`. In particular, it can be unnatural to think of - :app:`Pyramid` model objects as "models" if you develop your - application using :term:`traversal` and a relational database. When - you develop such applications, the object graph *might* be composed - completely of "model" objects (as defined by the ORM) but it also - might not be. The things that :app:`Pyramid` refers to as - "models" in such an application may instead just be stand-ins that - perform a query and generate some wrapper *for* an ORM "model" or - set of ORM models. This naming overlap is slightly unfortunate. - However, many :app:`Pyramid` applications (especially ones which - use :term:`ZODB`) do indeed traverse a graph full of literal model - nodes. Each node in the graph is a separate persistent object that - is stored within a database. This was the use case considered when - coming up with the "model" terminology. However, if we had it to do - all over again, we'd probably call these objects something - different to avoid confusion. - -.. index:: - single: model constructor - -Defining a Model Constructor ----------------------------- - -An example of a model constructor, ``BlogEntry`` is presented below. -It is implemented as a class which, when instantiated, becomes a model -instance. - -.. code-block:: python - :linenos: - - import datetime - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - -A model constructor may be essentially any Python object which is -callable, and which returns a model instance. In the above example, -the ``BlogEntry`` class can be called, returning a model instance. - -.. index:: - single: model interfaces - -.. _models_which_implement_interfaces: - -Model Instances Which Implement Interfaces ------------------------------------------- - -Model instances can optionally be made to implement an -:term:`interface`. An interface is used to tag a model object with a -"type" that can later be referred to within :term:`view -configuration`. - -Specifying an interface instead of a class as the ``context`` or -``containment`` arguments within :term:`view configuration` statements -effectively makes it possible to use a single view callable for more -than one class of object. If your application is simple enough that -you see no reason to want to do this, you can skip reading this -section of the chapter. - -For example, here's some code which describes a blog entry which also -declares that the blog entry implements an :term:`interface`. - -.. code-block:: python - :linenos: - - import datetime - from zope.interface import implements - from zope.interface import Interface - - class IBlogEntry(Interface): - pass - - class BlogEntry(object): - implements(IBlogEntry) - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - -This model consists of two things: the class which defines the model -constructor (above as the class ``BlogEntry``), and an -:term:`interface` attached to the class (via an ``implements`` -statement at class scope using the ``IBlogEntry`` interface as its -sole argument). - -The interface object used must be an instance of a class that inherits -from :class:`zope.interface.Interface`. - -A model class may *implement* zero or more interfaces. You specify -that a model implements an interface by using the -:func:`zope.interface.implements` function at class scope. The above -``BlogEntry`` model implements the ``IBlogEntry`` interface. - -You can also specify that a *particular* model instance provides an -interface, as opposed to its class as above, which implies that all -instances do. To do so, use the :func:`zope.interface.directlyProvides` -function: - -.. code-block:: python - :linenos: - - from zope.interface import directlyProvides - from zope.interface import Interface - - class IBlogEntry(Interface): - pass - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - - entry = BlogEntry('title', 'body', 'author') - directlyProvides(entry, IBlogEntry) - -:func:`zope.interface.directlyProvides` will replace any existing -interface that was previously provided by an instance. If a model -object already has instance-level interface declarations that you -don't want to replace, use the :func:`zope.interface.alsoProvides` -function: - -.. code-block:: python - :linenos: - - from zope.interface import alsoProvides - from zope.interface import directlyProvides - from zope.interface import Interface - - class IBlogEntry1(Interface): - pass - - class IBlogEntry2(Interface): - pass - - class BlogEntry(object): - def __init__(self, title, body, author): - self.title = title - self.body = body - self.author = author - self.created = datetime.datetime.now() - - entry = BlogEntry('title', 'body', 'author') - directlyProvides(entry, IBlogEntry1) - alsoProvides(entry, IBlogEntry2) - -:func:`zope.interface.alsoProvides` will augment the set of interfaces -directly provided by an instance instead of overwriting them like -:func:`zope.interface.directlyProvides` does. - -For more information about how model interfaces can be used by view -configuration, see :ref:`using_model_interfaces`. - -.. index:: - single: model graph - single: traversal graph - single: object graph - single: container nodes - single: leaf nodes - -Defining a Graph of Model Instances for Traversal -------------------------------------------------- - -When :term:`traversal` is used (as opposed to a purely :term:`url -dispatch` based application), :app:`Pyramid` expects to be able to -traverse a graph composed of model instances. Traversal begins at a -root model instance, and descends into the graph recursively via each -found model's ``__getitem__`` method. :app:`Pyramid` imposes the -following policy on model instance nodes in the graph: - -- Container nodes (i.e., nodes which contain other nodes) must supply - a ``__getitem__`` method which is willing to resolve a unicode name - to a subobject. If a subobject by that name does not exist in the - container, ``__getitem__`` must raise a :exc:`KeyError`. If a - subobject by that name *does* exist, the container should return the - subobject (another model instance). - -- Leaf nodes, which do not contain other nodes, must not - implement a ``__getitem__``, or if they do, their ``__getitem__`` - method must raise a :exc:`KeyError`. - -See :ref:`traversal_chapter` for more information about how traversal -works against model instances. - -.. index:: - pair: location-aware; model - -.. _location_aware: - -Location-Aware Model Instances ------------------------------- - -Applications which use :term:`traversal` to locate the :term:`context` -of a view must ensure that the model instances that make up the model -graph are "location aware". - -In order for :app:`Pyramid` location, security, URL-generation, and -traversal functions (i.e., functions in :ref:`location_module`, -:ref:`traversal_module`, :ref:`url_module` and some in -:ref:`security_module` ) to work properly against the model instances in -an object graph, all nodes in the graph must be :term:`location` -aware. -This means they must have two attributes: ``__parent__`` and -``__name__``. - -The ``__parent__`` attribute should be a reference to the node's -parent model instance in the graph. The ``__name__`` attribute should -be the name that a node's parent refers to the node via -``__getitem__``. - -The ``__parent__`` of the root object should be ``None`` and its -``__name__`` should be the empty string. For instance: - -.. code-block:: python - :linenos: - - class MyRootObject(object): - __name__ = '' - __parent__ = None - -A node returned from the root item's ``__getitem__`` method should have -a ``__parent__`` attribute that is a reference to the root object, and -its ``__name__`` attribute should match the name by which it is -reachable via the root object's ``__getitem__``. A container under the -root should have a ``__getitem__`` that returns objects with a -``__parent__`` attribute that points at the container, and these -subobjects should have a ``__name__`` attribute that matches the name by -which they are retrieved from the container via ``__getitem__``. -This pattern continues recursively down the graph. - -The ``__parent__`` attributes of each node form a linked list -that points "upward" toward the root. This is analogous to the -`..` entry in filesystem directories. If you follow the ``__parent__`` -values from any node in the traversal graph, you will eventually -come to the root node, just like if you keep executing the -``cd ..`` filesystem command, eventually you will reach the -filesystem root directory. - -.. warning:: If your root model object has a ``__name__`` argument - that is not ``None`` or the empty string, URLs returned by the - :func:`pyramid.url.model_url` function and paths generated by - the :func:`pyramid.traversal.model_path` and - :func:`pyramid.traversal.model_path_tuple` APIs will be - generated improperly. The value of ``__name__`` will be prepended - to every path and URL generated (as opposed to a single leading - slash or empty tuple element). - -.. sidebar:: Using :mod:`pyramid_traversalwrapper` - - If you'd rather not manage the ``__name__`` and ``__parent__`` - attributes of your models "by hand", an add-on package named - :mod:`pyramid_traversalwrapper` can help. - - In order to use this helper feature, you must first install the - :mod:`pyramid_traversalwrapper` package (available via PyPI), then register - its ``ModelGraphTraverser`` as the traversal policy, rather than the - default :app:`Pyramid` traverser. The package contains instructions for - doing so. - - Once :app:`Pyramid` is configured with this feature, you will no - longer need to manage the ``__parent__`` and ``__name__`` attributes - on graph objects "by hand". Instead, as necessary, during traversal - :app:`Pyramid` will wrap each object (even the root object) in a - ``LocationProxy`` which will dynamically assign a ``__name__`` and a - ``__parent__`` to the traversed object (based on the last traversed - object and the name supplied to ``__getitem__``). The root object - will have a ``__name__`` attribute of ``None`` and a ``__parent__`` - attribute of ``None``. - -.. index:: - single: model API functions - single: url generation (traversal) - -:app:`Pyramid` API Functions That Act Against Models -------------------------------------------------------- - -A model instance is used as the :term:`context` provided to a view. See -:ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information -about how a model instance becomes the context. - -The APIs provided by :ref:`traversal_module` are used against model -instances. These functions can be used to find the "path" of a model, -the root model in an object graph, or generate a URL to a model. - -The APIs provided by :ref:`location_module` are used against model -instances. These can be used to walk down an object graph, or -conveniently locate one object "inside" another. - -Some APIs in :ref:`security_module` accept a model object as a -parameter. For example, the -:func:`pyramid.security.has_permission` API accepts a "context" (a -model object) as one of its arguments; the ACL is obtained from this -model or one of its ancestors. Other APIs in the -:mod:`pyramid.security` module also accept :term:`context` as an -argument, and a context is always a model. - diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 2b03f7373..ff03a57b8 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -258,8 +258,8 @@ The Interactive Shell Once you've installed your program for development using ``setup.py develop``, you can use an interactive Python shell to examine your -:app:`Pyramid` application :term:`model` and :term:`view` objects from -a Python prompt. To do so, use the ``paster`` shell command with the +:app:`Pyramid` application :term:`resource` and :term:`view` objects from a +Python prompt. To do so, use the ``paster`` shell command with the ``pshell`` argument: The first argument to ``pshell`` is the path to your application's ``.ini`` @@ -290,7 +290,7 @@ the name ``MyProject`` as a section name: Type "help" for more information. "root" is the Pyramid app root object, "registry" is the Pyramid registry object. >>> root - <myproject.models.MyModel object at 0x445270> + <myproject.resources.MyResource object at 0x445270> >>> registry <Registry myproject> >>> registry.settings['debug_notfound'] @@ -468,7 +468,7 @@ structure: |-- development.ini |-- myproject | |-- __init__.py - | |-- models.py + | |-- resources.py | |-- static | | |-- favicon.ico | | |-- logo.png @@ -738,7 +738,7 @@ The ``myproject`` :term:`package` lives inside the ``MyProject`` 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. @@ -771,8 +771,8 @@ also informs Python that the directory which contains it is a *package*. #. Line 1 imports the :term:`Configurator` class from :mod:`pyramid.config` that we use later. -#. Line 2 imports the ``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 @@ -784,19 +784,19 @@ also informs Python that the directory which contains it is a *package*. 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 @@ -817,7 +817,7 @@ code which accepts a :term:`request` and which returns a 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: @@ -846,43 +846,38 @@ renderers, and templates relate and cooperate. 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. - -#. 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``. +#. Lines 1-3 define the Root class. The Root class is a "root resource + factory" function that will be called by the :app:`Pyramid` *Router* for + each request when it wants to find the root of the resource tree. -In a "real" application, the root object would 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 diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index c87157472..21ba97c9e 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -1,274 +1,319 @@ -.. index:: - single: resources - -.. _resources_chapter: - Resources ========= -A :term:`resource` is any file contained within a Python -:term:`package` which is *not* a Python source code file. For -example, each of the following is a resource: - -- a :term:`Chameleon` template file contained within a Python package. - -- a GIF image file contained within a Python package. - -- a CSS file contained within a Python package. - -- a JavaScript source file contained within a Python package. - -- A directory within a package that does not have an ``__init__.py`` - in it (if it possessed an ``__init__.py`` it would *be* a package). +A :term:`resource` is an object that represents a "place" in your +application. Every :app:`Pyramid` application has at least one resource +object: the :term:`root` resource. The root resource is the root of a +:term:`resource tree`. A resource tree is a set of nested dictionary-like +objects which you may use to represent your website's structure. + +In an application which uses :term:`traversal` to map URLs to code, the +resource tree structure is used heavily to map a URL to a :term:`view +callable`. :app:`Pyramid` will walk "up" the resource tree when +:term:`traversal` is used in order to find a :term:`context`. Once a context +is found, the resource represented by the context combined with data in the +request will be used to find a :term:`view callable`. + +In an application which uses :term:`URL dispatch`, the resource tree is only +used indirectly, and is often "invisible" to the developer. In URL dispatch +applications, the resource "tree" is often composed of only the root resource +by itself. This root resource sometimes has security declarations attached +to it, but is not required to have any. In general, the resource tree is +much less important in applications that use URL dispatch than applications +that use traversal. + +In "Zope-like" :app:`Pyramid` applications, resource objects also often store +data persistently and offer methods related to mutating that persistent data. +In these kinds of applications, resources not only represent the site +structure of your website, but they become the :term:`domain model` of the +application. + +Also: + +- The ``context`` and ``containment`` predicate arguments to + :meth:`pyramid.config.Configurator.add_view` (or a + :func:`pyramid.view.view_config` decorator) and reference a resource class + or resource :term:`interface`. + +- A :term:`root factory` returns a resource. + +- A resource is exposed to :term:`view` code as the :term:`context` of a + view. -The use of resources is quite common in most web development projects. -For example, when you create a :app:`Pyramid` application using one -of the available "paster" templates, as described in -:ref:`creating_a_project`, the directory representing the application -contains a Python :term:`package`. Within that Python package, there -are directories full of files which are resources. For example, there -is a ``templates`` directory which contains ``.pt`` files, and a -``static`` directory which contains ``.css``, ``.js``, and ``.gif`` -files. - -.. _understanding_resources: +.. index:: + single: resource constructor -Understanding Resources ------------------------ +Defining a Resource Constructor +------------------------------- -Let's imagine you've created a :app:`Pyramid` application that uses -a :term:`Chameleon` ZPT template via the -:func:`pyramid.chameleon_zpt.render_template_to_response` API. For -example, the application might address the resource named -``templates/some_template.pt`` using that API within a ``views.py`` -file inside a ``myapp`` package: +An example of a resource constructor, ``BlogEntry`` is presented below. It +is implemented as a class which, when instantiated, becomes a resource +instance. -.. ignore-next-block .. code-block:: python :linenos: - from pyramid.chameleon_zpt import render_template_to_response - render_template_to_response('templates/some_template.pt') - -"Under the hood", when this API is called, :app:`Pyramid` attempts -to make sense out of the string ``templates/some_template.pt`` -provided by the developer. To do so, it first finds the "current" -package. The "current" package is the Python package in which the -``views.py`` module which contains this code lives. This would be the -``myapp`` package, according to our example so far. By resolving the -current package, :app:`Pyramid` has enough information to locate -the actual template file. These are the elements it needs: - -- The *package name* (``myapp``) + import datetime -- The *resource name* (``templates/some_template.pt``) + class BlogEntry(object): + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() -:app:`Pyramid` uses the :term:`pkg_resources` API to resolve the -package name and resource name to an absolute -(operating-system-specific) file name. It eventually passes this -resolved absolute filesystem path to the Chameleon templating engine, -which then uses it to load, parse, and execute the template file. - -Package names often contain dots. For example, ``pyramid`` is a -package. Resource names usually look a lot like relative UNIX file -paths. +A resource constructor may be any Python object which is callable, and which +returns a resource instance. In the above example, the ``BlogEntry`` class +can be "called", returning a resource instance. .. index:: - pair: overriding; resources + single: resource interfaces -.. _overriding_resources_section: +.. _resources_which_implement_interfaces: -Overriding Resources --------------------- +Resources Which Implement Interfaces +------------------------------------ -It can often be useful to override specific resources "from outside" a -given :app:`Pyramid` application. For example, you may wish to -reuse an existing :app:`Pyramid` application more or less -unchanged. However, some specific template file owned by the -application might have inappropriate HTML, or some static resource -(such as a logo file or some CSS file) might not be appropriate. You -*could* just fork the application entirely, but it's often more -convenient to just override the resources that are inappropriate and -reuse the application "as is". This is particularly true when you -reuse some "core" application over and over again for some set of -customers (such as a CMS application, or some bug tracking -application), and you want to make arbitrary visual modifications to a -particular application deployment without forking the underlying code. +Resources can optionally be made to implement an :term:`interface`. An +interface is used to tag a resource object with a "type" that can later be +referred to within :term:`view configuration`. -To this end, :app:`Pyramid` contains a feature that makes it -possible to "override" one resource with one or more other resources. -In support of this feature, a :term:`ZCML` directive exists named -``resource``. The ``resource`` directive allows you to *override* the -following kinds of resources defined in any Python package: +Specifying an interface instead of a class as the ``context`` or +``containment`` predicate arguments within :term:`view configuration` +statements effectively makes it possible to use a single view callable for +more than one class of resource object. If your application is simple enough +that you see no reason to want to do this, you can skip reading this section +of the chapter. -- Individual :term:`Chameleon` templates. +For example, here's some code which describes a blog entry which also +declares that the blog entry implements an :term:`interface`. -- A directory containing multiple Chameleon templates. +.. code-block:: python + :linenos: -- Individual static files served up by an instance of the - ``pyramid.view.static`` helper class. + import datetime + from zope.interface import implements + from zope.interface import Interface -- A directory of static files served up by an instance of the - ``pyramid.view.static`` helper class. + class IBlogEntry(Interface): + pass -- Any other resource (or set of resources) addressed by code that uses - the setuptools :term:`pkg_resources` API. + class BlogEntry(object): + implements(IBlogEntry) + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() -.. index:: - single: override_resource +This resource consists of two things: the class which defines the resource +constructor as the class ``BlogEntry``, and an :term:`interface` attached to +the class via an ``implements`` statement at class scope using the +``IBlogEntry`` interface as its sole argument. -.. _override_resource: +The interface object used must be an instance of a class that inherits from +:class:`zope.interface.Interface`. -The ``override_resource`` API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A resource class may implement zero or more interfaces. You specify that a +resource implements an interface by using the +:func:`zope.interface.implements` function at class scope. The above +``BlogEntry`` resource implements the ``IBlogEntry`` interface. -An individual call to -:meth:`pyramid.config.Configurator.override_resource` can -override a single resource. For example: +You can also specify that a particular resource *instance* provides an +interface, as opposed to its class. When you declare that a class implements +an interface, all instances of that class will also provide that interface. +However, you can also just say that a single object provides the interface. +To do so, use the :func:`zope.interface.directlyProvides` function: -.. ignore-next-block .. code-block:: python :linenos: - config.override_resource( - to_override='some.package:templates/mytemplate.pt', - override_with='another.package:othertemplates/anothertemplate.pt') + from zope.interface import directlyProvides + from zope.interface import Interface -The string value passed to both ``to_override`` and ``override_with`` -attached to a resource directive is called a "specification". The -colon separator in a specification separates the *package name* from -the *resource name*. The colon and the following resource name are -optional. If they are not specified, the override attempts to resolve -every lookup into a package from the directory of another package. -For example: + class IBlogEntry(Interface): + pass -.. ignore-next-block -.. code-block:: python - :linenos: + class BlogEntry(object): + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() - config.override_resource(to_override='some.package', - override_with='another.package') + entry = BlogEntry('title', 'body', 'author') + directlyProvides(entry, IBlogEntry) -Individual subdirectories within a package can also be overridden: +:func:`zope.interface.directlyProvides` will replace any existing interface +that was previously provided by an instance. If a resource object already +has instance-level interface declarations that you don't want to replace, use +the :func:`zope.interface.alsoProvides` function: -.. ignore-next-block .. code-block:: python :linenos: - config.override_resource(to_override='some.package:templates/', - override_with='another.package:othertemplates/') - + from zope.interface import alsoProvides + from zope.interface import directlyProvides + from zope.interface import Interface -If you wish to override a directory with another directory, you *must* -make sure to attach the slash to the end of both the ``to_override`` -specification and the ``override_with`` specification. If you fail to -attach a slash to the end of a specification that points to a directory, -you will get unexpected results. + class IBlogEntry1(Interface): + pass -You cannot override a directory specification with a file -specification, and vice versa: a startup error will occur if you try. -You cannot override a resource with itself: a startup error will occur -if you try. + class IBlogEntry2(Interface): + pass -Only individual *package* resources may be overridden. Overrides will -not traverse through subpackages within an overridden package. This -means that if you want to override resources for both -``some.package:templates``, and ``some.package.views:templates``, you -will need to register two overrides. - -The package name in a specification may start with a dot, meaning that -the package is relative to the package in which the configuration -construction file resides (or the ``package`` argument to the -:class:`pyramid.config.Configurator` class construction). -For example: - -.. ignore-next-block -.. code-block:: python - :linenos: + class BlogEntry(object): + def __init__(self, title, body, author): + self.title = title + self.body = body + self.author = author + self.created = datetime.datetime.now() - config.override_resource(to_override='.subpackage:templates/', - override_with='another.package:templates/') + entry = BlogEntry('title', 'body', 'author') + directlyProvides(entry, IBlogEntry1) + alsoProvides(entry, IBlogEntry2) -Multiple ``override_resource`` statements which name a shared -``to_override`` but a different ``override_with`` specification can be -"stacked" to form a search path. The first resource that exists in -the search path will be used; if no resource exists in the override -path, the original resource is used. +:func:`zope.interface.alsoProvides` will augment the set of interfaces +directly provided by an instance instead of overwriting them like +:func:`zope.interface.directlyProvides` does. -Resource overrides can actually override resources other than -templates and static files. Any software which uses the -:func:`pkg_resources.get_resource_filename`, -:func:`pkg_resources.get_resource_stream` or -:func:`pkg_resources.get_resource_string` APIs will obtain an -overridden file when an override is used. +For more information about how resource interfaces can be used by view +configuration, see :ref:`using_resource_interfaces`. .. index:: - pair: ZCML directive; resource + single: resource tree + single: traversal tree + single: object tree + single: container resources + single: leaf resources + +Defining a Resource Tree +------------------------ + +When :term:`traversal` is used (as opposed to a purely :term:`url dispatch` +based application), :app:`Pyramid` expects to be able to traverse a tree +composed of resources (the :term:`resource tree`). Traversal begins at a +root resource, and descends into the tree recursively via each resource's +``__getitem__`` method. :app:`Pyramid` imposes the following policy on +resource instances in the tree: + +- A container resource (a resource which contains other resources) must + supply a ``__getitem__`` method which is willing to resolve a unicode name + to a sub-resource. If a sub-resource by a particular name does not exist + in a container resource, ``__getitem__`` method of the container resource + must raise a :exc:`KeyError`. If a sub-resource by that name *does* exist, + the container's ``__getitem__`` should return the sub-resource. + +- Leaf resources, which do not contain other resources, must not implement a + ``__getitem__``, or if they do, their ``__getitem__`` method must raise a + :exc:`KeyError`. + +See :ref:`traversal_chapter` for more information about how traversal +works against resource instances. -.. _resource_zcml_directive: +.. index:: + pair: location-aware; resource -The ``resource`` ZCML Directive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _location_aware: -Instead of using -:meth:`pyramid.config.Configurator.override_resource` during -:term:`imperative configuration`, an equivalent can be used to perform -all the tasks described above within :term:`ZCML`. The ZCML -``resource`` tag is a frontend to using ``override_resource``. +Location-Aware Resources +------------------------ -An individual :app:`Pyramid` ``resource`` ZCML statement can -override a single resource. For example: +Applications which use :term:`traversal` to locate the :term:`context` +resource of a view must ensure that the resources that make up the +resource tree are "location aware". -.. code-block:: xml - :linenos: +In order for :app:`Pyramid` location, security, URL-generation, and traversal +functions (i.e., functions in :ref:`location_module`, +:ref:`traversal_module`, :ref:`url_module` and some in :ref:`security_module` +) to work properly against the resources in a resource tree, all resources in +the tree must be :term:`location` -aware. This means they must have two +attributes: ``__parent__`` and ``__name__``. - <resource - to_override="some.package:templates/mytemplate.pt" - override_with="another.package:othertemplates/anothertemplate.pt" - /> +The ``__parent__`` attribute should be a reference to the resource's parent +resource instance in the tree. The ``__name__`` attribute should be the name +with which a resource's parent refers to the resource via ``__getitem__``. -The string value passed to both ``to_override`` and ``override_with`` -attached to a resource directive is called a "specification". The -colon separator in a specification separates the *package name* from -the *resource name*. The colon and the following resource name are -optional. If they are not specified, the override attempts to resolve -every lookup into a package from the directory of another package. -For example: +The ``__parent__`` of the root resource should be ``None`` and its +``__name__`` should be the empty string. For instance: -.. code-block:: xml +.. code-block:: python :linenos: - <resource - to_override="some.package" - override_with="another.package" - /> - -Individual subdirectories within a package can also be overridden: + class MyRootResource(object): + __name__ = '' + __parent__ = None + +A resource returned from the root resource's ``__getitem__`` method should +have a ``__parent__`` attribute that is a reference to the root resource, and +its ``__name__`` attribute should match the name by which it is reachable via +the root resource's ``__getitem__``. A container resource within the root +resource should have a ``__getitem__`` that returns resources with a +``__parent__`` attribute that points at the container, and these subobjects +should have a ``__name__`` attribute that matches the name by which they are +retrieved from the container via ``__getitem__``. This pattern continues +recursively "up" the tree from the root. + +The ``__parent__`` attributes of each resource form a linked list that points +"upward" toward the root. This is analogous to the `..` entry in filesystem +directories. If you follow the ``__parent__`` values from any resource in the +resource tree, you will eventually come to the root resource, just like if +you keep executing the ``cd ..`` filesystem command, eventually you will +reach the filesystem root directory. + +.. warning:: If your root resource has a ``__name__`` argument + that is not ``None`` or the empty string, URLs returned by the + :func:`pyramid.url.resource_url` function and paths generated by + the :func:`pyramid.traversal.resource_path` and + :func:`pyramid.traversal.resource_path_tuple` APIs will be + generated improperly. The value of ``__name__`` will be prepended + to every path and URL generated (as opposed to a single leading + slash or empty tuple element). + +.. sidebar:: Using :mod:`pyramid_traversalwrapper` + + If you'd rather not manage the ``__name__`` and ``__parent__`` attributes + of your resources "by hand", an add-on package named + :mod:`pyramid_traversalwrapper` can help. + + In order to use this helper feature, you must first install the + :mod:`pyramid_traversalwrapper` package (available via PyPI), then register + its ``ModelGraphTraverser`` as the traversal policy, rather than the + default :app:`Pyramid` traverser. The package contains instructions for + doing so. + + Once :app:`Pyramid` is configured with this feature, you will no longer + need to manage the ``__parent__`` and ``__name__`` attributes on resource + objects "by hand". Instead, as necessary, during traversal :app:`Pyramid` + will wrap each resource (even the root resource) in a ``LocationProxy`` + which will dynamically assign a ``__name__`` and a ``__parent__`` to the + traversed resrouce (based on the last traversed resource and the name + supplied to ``__getitem__``). The root resource will have a ``__name__`` + attribute of ``None`` and a ``__parent__`` attribute of ``None``. -.. code-block:: xml - :linenos: +.. index:: + single: resource API functions + single: url generation (traversal) - <resource - to_override="some.package:templates/" - override_with="another.package:othertemplates/" - /> +:app:`Pyramid` API Functions That Act Against Resources +------------------------------------------------------- -If you wish to override a directory with another directory, you *must* -make sure to attach the slash to the end of both the ``to_override`` -specification and the ``override_with`` specification. If you fail to -attach a slash to the end of a specification that points to a directory, -you will get unexpected results. +A resource object is used as the :term:`context` provided to a view. See +:ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information +about how a resource object becomes the context. -The package name in a specification may start with a dot, meaning that -the package is relative to the package in which the ZCML file resides. -For example: +The APIs provided by :ref:`traversal_module` are used against resource +objects. These functions can be used to find the "path" of a resource, the +root resource in a resource tree, or to generate a URL for a resource. -.. code-block:: xml - :linenos: +The APIs provided by :ref:`location_module` are used against resources. +These can be used to walk down a resource tree, or conveniently locate one +resource "inside" another. - <resource - to_override=".subpackage:templates/" - override_with="another.package:templates/" - /> +Some APIs in :ref:`security_module` accept a resource object as a parameter. +For example, the :func:`pyramid.security.has_permission` API accepts a +resource object as one of its arguments; the ACL is obtained from this +resource or one of its ancestors. Other APIs in the :mod:`pyramid.security` +module also accept :term:`context` as an argument, and a context is always a +resource. -See also :ref:`resource_directive`. diff --git a/docs/narr/modelgraphtraverser.png b/docs/narr/resourcetreetraverser.png Binary files differindex 037d12b18..037d12b18 100644 --- a/docs/narr/modelgraphtraverser.png +++ b/docs/narr/resourcetreetraverser.png diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 5edbc3ec3..469d3d298 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -92,11 +92,11 @@ For example: Python name` values, each representing the dotted name path to a suitable implementation global defined at Python module scope. -The above configuration enables a policy which compares the value of -an "auth ticket" cookie passed in the request's environment which -contains a reference to a single :term:`principal` against the -principals present in any :term:`ACL` found in model data when -attempting to call some :term:`view`. +The above configuration enables a policy which compares the value of an "auth +ticket" cookie passed in the request's environment which contains a reference +to a single :term:`principal` against the principals present in any +:term:`ACL` found in the resource tree when attempting to call some +:term:`view`. While it is possible to mix and match different authentication and authorization policies, it is an error to pass an authentication @@ -136,7 +136,7 @@ permission using the :meth:`pyramid.config.Configurator.add_view` API: config.add_view('mypackage.views.blog_entry_add_view', name='add_entry.html', - context='mypackage.models.Blog', + context='mypackage.resources.Blog', permission='add') The equivalent view registration including the ``add`` permission name @@ -147,7 +147,7 @@ may be performed via the ``@view_config`` decorator: :linenos: from pyramid.view import view_config - from models import Blog + from resources import Blog @view_config(context=Blog, name='add_entry.html', permission='add') def blog_entry_add_view(request): @@ -208,19 +208,18 @@ When a default permission is registered: .. _assigning_acls: -Assigning ACLs to your Model Objects ------------------------------------- +Assigning ACLs to your Resource Objects +--------------------------------------- -When the default :app:`Pyramid` :term:`authorization policy` -determines whether a user possesses a particular permission in a -:term:`context`, it examines the :term:`ACL` associated with the -context. An ACL is associated with a context by virtue of the -``__acl__`` attribute of the model object representing the -:term:`context`. This attribute can be defined on the model -*instance* if you need instance-level security, or it can be defined -on the model *class* if you just need type-level security. +When the default :app:`Pyramid` :term:`authorization policy` determines +whether a user possesses a particular permission in a :term:`context`, it +examines the :term:`ACL` associated with the context. An ACL is associated +with a context by virtue of the ``__acl__`` attribute of the resource object +representing the :term:`context`. This attribute can be defined on the +resource *instance* if you need instance-level security, or it can be defined +on the resource *class* if you just need type-level security. -For example, an ACL might be attached to the model for a blog via its +For example, an ACL might be attached to the resource for a blog via its class: .. code-block:: python @@ -236,8 +235,8 @@ class: (Allow, 'group:editors', 'edit'), ] -Or, if your models are persistent, an ACL might be specified via the -``__acl__`` attribute of an *instance* of a model: +Or, if your resources are persistent, an ACL might be specified via the +``__acl__`` attribute of an *instance* of a resource: .. code-block:: python :linenos: @@ -256,11 +255,11 @@ Or, if your models are persistent, an ACL might be specified via the (Allow, 'group:editors', 'edit'), ] -Whether an ACL is attached to a model's class or an instance of the -model itself, the effect is the same. It is useful to decorate -individual model instances with an ACL (as opposed to just decorating -their class) in applications such as "CMS" systems where fine-grained -access is required on an object-by-object basis. +Whether an ACL is attached to a resource's class or an instance of the +resource itself, the effect is the same. It is useful to decorate individual +resource instances with an ACL (as opposed to just decorating their class) in +applications such as "CMS" systems where fine-grained access is required on +an object-by-object basis. .. index:: single: ACE @@ -448,16 +447,16 @@ the following: ACL Inheritance and Location-Awareness -------------------------------------- -While the default :term:`authorization policy` is in place, if a model -object does not have an ACL when it is the context, its *parent* is -consulted for an ACL. If that object does not have an ACL, *its* -parent is consulted for an ACL, ad infinitum, until we've reached the -root and there are no more parents left. +While the default :term:`authorization policy` is in place, if a resource +object does not have an ACL when it is the context, its *parent* is consulted +for an ACL. If that object does not have an ACL, *its* parent is consulted +for an ACL, ad infinitum, until we've reached the root and there are no more +parents left. -In order to allow the security machinery to perform ACL inheritance, -model objects must provide *location-awareness*. Providing -*location-awareness* means two things: the root object in the graph -must have a ``_name__`` attribute and a ``__parent__`` attribute. +In order to allow the security machinery to perform ACL inheritance, resource +objects must provide *location-awareness*. Providing *location-awareness* +means two things: the root object in the resource tree must have a +``_name__`` attribute and a ``__parent__`` attribute. .. code-block:: python :linenos: diff --git a/docs/narr/static.rst b/docs/narr/static.rst index 172fb08d4..6b03b2349 100644 --- a/docs/narr/static.rst +++ b/docs/narr/static.rst @@ -1,26 +1,26 @@ -Static Resources -================ +Static Assets +============= -:app:`Pyramid` makes it possible to serve up static -resources files from a directory on a filesystem. This chapter describes -how to configure :app:`Pyramid` to do so. +:app:`Pyramid` makes it possible to serve up static asset files from a +directory on a filesystem. This chapter describes how to configure +:app:`Pyramid` to do so. .. index:: single: add_static_view -.. _static_resources_section: +.. _static_assets_section: -Serving Static Resources ------------------------- +Serving Static Assets +--------------------- -Use the :meth:`pyramid.config.Configurator.add_static_view` to -instruct :app:`Pyramid` to serve static resources such as JavaScript and CSS -files. This mechanism makes static files available at a name relative to the -application root URL, e.g. ``/static``. +Use the :meth:`pyramid.config.Configurator.add_static_view` to instruct +:app:`Pyramid` to serve static assets such as JavaScript and CSS files. This +mechanism makes static files available at a name relative to the application +root URL, e.g. ``/static``. Note that the ``path`` provided to -:meth:`pyramid.config.Configurator.add_static_view` may be a fully -qualified :term:`resource specification`, or an *absolute path*. +:meth:`pyramid.config.Configurator.add_static_view` may be a fully qualified +:term:`asset specification`, or an *absolute path*. Here's an example of a use of :meth:`pyramid.config.Configurator.add_static_view` that will serve @@ -34,11 +34,10 @@ path. # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='/var/www/static') -Here's an example of -:meth:`pyramid.config.Configurator.add_static_view` that will serve -files up under the ``/static`` URL from the ``a/b/c/static`` directory of the -Python package named ``some_package`` using a fully qualified :term:`resource -specification`. +Here's an example of :meth:`pyramid.config.Configurator.add_static_view` that +will serve files up under the ``/static`` URL from the ``a/b/c/static`` +directory of the Python package named ``some_package`` using a fully +qualified :term:`asset specification`. .. code-block:: python :linenos: @@ -46,7 +45,7 @@ specification`. # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='some_package:a/b/c/static') -Whether you use for ``path`` a fully qualified resource specification, or an +Whether you use for ``path`` a fully qualified asset specification, or an absolute path, when you place your static files on the filesystem in the directory represented as the ``path`` of the directive, you will then be able to view the static files in this directory via a browser at URLs prefixed @@ -58,13 +57,12 @@ subdirectories recursively, and any subdirectories may hold files; these will be resolved by the static view as you would expect. While the ``path`` argument can be a number of different things, the ``name`` -argument of the call to -:meth:`pyramid.config.Configurator.add_static_view` can also be one of -a number of things: a *view name* or a *URL*. The above examples have shown -usage of the ``name`` argument as a view name. When ``name`` is a *URL* (or -any string with a slash (``/``) in it), static resources can be served from -an external webserver. In this mode, the ``name`` is used as the URL prefix -when generating a URL using :func:`pyramid.url.static_url`. +argument of the call to :meth:`pyramid.config.Configurator.add_static_view` +can also be one of a number of things: a *view name* or a *URL*. The above +examples have shown usage of the ``name`` argument as a view name. When +``name`` is a *URL* (or any string with a slash (``/``) in it), static assets +can be served from an external webserver. In this mode, the ``name`` is used +as the URL prefix when generating a URL using :func:`pyramid.url.static_url`. .. note:: @@ -130,18 +128,18 @@ imperative configuration for the same purpose. other than ``media_location`` could be used. .. index:: - single: generating static resource urls - single: static resource urls + single: generating static asset urls + single: static asset urls -.. _generating_static_resource_urls: +.. _generating_static_asset_urls: -Generating Static Resource URLs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Generating Static Asset URLs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :meth:`pyramid.config.Configurator.add_static_view` method is -used to register a static resource directory, a special helper API named -:func:`pyramid.url.static_url` can be used to generate the appropriate URL for a -package resource that lives in one of the directories named by the static +When a :meth:`pyramid.config.Configurator.add_static_view` method is used to +register a static asset directory, a special helper API named +:func:`pyramid.url.static_url` can be used to generate the appropriate URL +for an asset that lives in one of the directories named by the static registration ``path`` attribute. For example, let's assume you create a set of static declarations like so: @@ -149,19 +147,19 @@ For example, let's assume you create a set of static declarations like so: .. code-block:: python :linenos: - config.add_static_view(name='static1', path='mypackage:resources/1') - config.add_static_view(name='static2', path='mypackage:resources/2') + config.add_static_view(name='static1', path='mypackage:assets/1') + config.add_static_view(name='static2', path='mypackage:assets/2') These declarations create URL-accessible directories which have URLs that -begin with ``/static1`` and ``/static2``, respectively. The resources in -the ``resources/1`` directory of the ``mypackage`` package are consulted when -a user visits a URL which begins with ``/static1``, and the resources in the -``resources/2`` directory of the ``mypackage`` package are consulted when a -user visits a URL which begins with ``/static2``. +begin with ``/static1`` and ``/static2``, respectively. The assets in the +``assets/1`` directory of the ``mypackage`` package are consulted when a user +visits a URL which begins with ``/static1``, and the assets in the +``assets/2`` directory of the ``mypackage`` package are consulted when a user +visits a URL which begins with ``/static2``. -You needn't generate the URLs to static resources "by hand" in such a -configuration. Instead, use the :func:`pyramid.url.static_url` API -to generate them for you. For example: +You needn't generate the URLs to static assets "by hand" in such a +configuration. Instead, use the :func:`pyramid.url.static_url` API to +generate them for you. For example: .. code-block:: python :linenos: @@ -170,8 +168,8 @@ to generate them for you. For example: from pyramid.chameleon_zpt import render_template_to_response def my_view(request): - css_url = static_url('mypackage:resources/1/foo.css', request) - js_url = static_url('mypackage:resources/2/foo.js', request) + css_url = static_url('mypackage:assets/1/foo.css', request) + js_url = static_url('mypackage:assets/2/foo.js', request) return render_template_to_response('templates/my_template.pt', css_url = css_url, js_url = js_url) @@ -186,13 +184,12 @@ constructing static URLs "by hand" is that if you need to change the ``name`` of a static URL declaration, the generated URLs will continue to resolve properly after the rename. -URLs may also be generated by :func:`pyramid.url.static_url` to static -resources that live *outside* the :app:`Pyramid` application. This will -happen when the :meth:`pyramid.config.Configurator.add_static_view` -API associated with the path fed to :func:`pyramid.url.static_url` is a *URL* -instead of a view name. For example, the ``name`` argument may be -``http://example.com`` while the the ``path`` given may be -``mypackage:images``: +URLs may also be generated by :func:`pyramid.url.static_url` to static assets +that live *outside* the :app:`Pyramid` application. This will happen when +the :meth:`pyramid.config.Configurator.add_static_view` API associated with +the path fed to :func:`pyramid.url.static_url` is a *URL* instead of a view +name. For example, the ``name`` argument may be ``http://example.com`` while +the the ``path`` given may be ``mypackage:images``: .. code-block:: python :linenos: @@ -200,7 +197,7 @@ instead of a view name. For example, the ``name`` argument may be config.add_static_view(name='http://example.com/images', path='mypackage:images') Under such a configuration, the URL generated by ``static_url`` for -resources which begin with ``mypackage:images`` will be prefixed with +assets which begin with ``mypackage:images`` will be prefixed with ``http://example.com/images``: .. code-block:: python @@ -210,25 +207,23 @@ resources which begin with ``mypackage:images`` will be prefixed with # -> http://example.com/images/logo.png .. index:: - single: static resource view - -Advanced: Serving Static Resources Using a View Callable --------------------------------------------------------- - -For more flexibility, static resources can be served by a :term:`view -callable` which you register manually. For example, you may want -static resources to only be available when the :term:`context` of the -view is of a particular type, or when the request is of a particular -type. - -The :class:`pyramid.view.static` helper class is used to perform -this task. This class creates an object that is capable acting as a -:app:`Pyramid` view callable which serves static resources from a -directory. For instance, to serve files within a directory located on -your filesystem at ``/path/to/static/dir`` from the URL path -``/static`` in your application, create an instance of the -:class:`pyramid.view.static` class inside a ``static.py`` file in -your application root as below. + single: static assets view + +Advanced: Serving Static Assets Using a View Callable +----------------------------------------------------- + +For more flexibility, static assets can be served by a :term:`view callable` +which you register manually. For example, you may want static assets to only +be available when the :term:`context` is of a particular type, or when +certain request headers are present. + +The :class:`pyramid.view.static` helper class is used to perform this +task. This class creates an object that is capable acting as a :app:`Pyramid` +view callable which serves static assets from a directory. For instance, to +serve files within a directory located on your filesystem at +``/path/to/static/dir`` from the URL path ``/static`` in your application, +create an instance of the :class:`pyramid.view.static` class inside a +``static.py`` file in your application root as below. .. ignore-next-block .. code-block:: python @@ -240,47 +235,43 @@ your application root as below. .. note:: the argument to :class:`pyramid.view.static` can also be a "here-relative" pathname, e.g. ``my/static`` (meaning relative to the Python package of the module in which the view is being defined). - It can also be a :term:`resource specification` + It can also be a :term:`asset specification` (e.g. ``anotherpackage:some/subdirectory``). Subsequently, you may wire this view up to be accessible as ``/static`` using -the :mod:`pyramid.config.Configurator.add_view` method in your -application's startup code against either the class or interface that -represents your root object. +the :mod:`pyramid.config.Configurator.add_view` method in your application's +startup code against either the class or interface that represents your root +resource object. .. code-block:: python :linenos: config.add_view('mypackage.static.static_view', name='static', - context='mypackage.models.Root') + context='mypackage.resources.Root') -In this case, ``mypackage.models.Root`` refers to the class of your -:app:`Pyramid` application's traversal root object. +In this case, ``mypackage.resources.Root`` refers to the class of your +:app:`Pyramid` application's resource tree. -The context argument above limits where the static view is accessible to -URL paths directly under the root object. If you omit the ``context`` -argument, then ``static`` will be accessible as the static view against -any model object in the traversal graph. This will allow -``/static/foo.js`` to work, but it will also allow for -``/anything/static/foo.js`` too, as long as ``anything`` can be -resolved. +The context argument above limits where the static view is accessible to URL +paths directly under the root object. If you omit the ``context`` argument, +then ``static`` will be accessible as the static view against any resource +object in the resource tree. This will allow ``/static/foo.js`` to work, but +it will also allow for ``/anything/static/foo.js`` too, as long as +``anything`` can be resolved. Note that you cannot use the :func:`pyramid.url.static_url` API to generate -URLs against resources made accessible by registering a custom static view. +URLs against assets made accessible by registering a custom static view. .. warning:: - When adding a static view to your root object, you need to be - careful that there are no model objects contained in the - root with the same key as the view name (e.g., ``static``). - Model objects take precedence during traversal, - thus such a name collision will cause the model to "shadow" - your static view. To avoid this issue, and ensure that your - root object's ``__getitem__`` is never - called when a static resource is requested, you can refer to them - unambiguously using the ``@@`` prefix (goggles) in their URLs. - For the above examples you could use '/@@static/foo.js' - instead of '/static/foo.js' to avoid such shadowing. - See :ref:`traversal_chapter` for information - about "goggles" (``@@``). + When adding a static view to your root object, you need to be careful that + there are no resource objects contained in the root with the same key as + the view name (e.g., ``static``). Resource objects take precedence during + traversal, thus such a name collision will cause the resource to "shadow" + your static view. To avoid this issue, and ensure that your root + resource's ``__getitem__`` is never called when a static asset is + requested, you can refer to them unambiguously using the ``@@`` prefix + (goggles) in their URLs. For the above examples you could use + '/@@static/foo.js' instead of '/static/foo.js' to avoid such shadowing. + See :ref:`traversal_chapter` for information about "goggles" (``@@``). diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index 09b9bfc51..adec9aa0c 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -84,9 +84,9 @@ prefix on Windows. 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: @@ -98,20 +98,20 @@ address template resources which live in another package. For example: {'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 @@ -187,8 +187,8 @@ combination as shown above. :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 @@ -289,7 +289,7 @@ to specify the template as a :term:`renderer` in your 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 @@ -326,7 +326,7 @@ template renderer: 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 @@ -335,9 +335,9 @@ 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 @@ -468,21 +468,20 @@ works in these templates. Using ZPT Macros in :app:`Pyramid` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :term:`renderer` is used to render a template, :app:`Pyramid` -makes at least two top-level names available to the template by default: -``context`` and ``request``. One of the common needs in ZPT-based -templates is to use one template's "macros" from within a different -template. In Zope, this is typically handled by retrieving the template -from the ``context``. But the context in :app:`Pyramid` is typically a -model object, and templates cannot usually be retrieved from models. To -use macros in :app:`Pyramid`, you need to make the macro template itself -available to the rendered template by passing the macro template, or -even the macro itself, *into* the rendered template. To do this you can -use the :func:`pyramid.renderers.get_renderer` API to retrieve the macro -template, and pass it into the template being rendered via the dictionary -returned by the view. For example, using a :term:`view configuration` via a -:class:`pyramid.view.view_config` decorator that uses a -: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: @@ -658,7 +657,7 @@ on, an exception resulting from the same problem might end like so: 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 diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index f540fa255..171eaa1c7 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -110,11 +110,9 @@ follows: View callables already have access to the request (it's passed in to each as ``request``). -- ``get_current_request`` should never be called in :term:`model` - code. Model code should never require any access to the request; if - your model code requires access to a request object, you've almost - certainly factored something wrong, and you should change your code - rather than using this function. +- ``get_current_request`` should never be called in :term:`resource` code. + If a resource needs access to the request, it should be passed the request + by a :term:`view callable`. - ``get_current_request`` function should never be called because it's "easier" or "more elegant" to think about calling it than to pass a diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 01729c4bd..9bc6014cd 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -3,14 +3,14 @@ Traversal ========= -:term:`Traversal` is a :term:`context finding` mechanism. It is the -act of finding a :term:`context` and a :term:`view name` by walking -over an *object graph*, starting from a :term:`root` object, using a +:term:`Traversal` is a :term:`context finding` mechanism. It is the act of +finding a :term:`context` and a :term:`view name` by walking over a +:term:`resource tree`, starting from a :term:`root` resource, using a :term:`request` object as a source of path information. -In this chapter, we'll provide a high-level overview of traversal, -we'll explain the concept of an *object graph*, and we'll show how -traversal might be used within an application. +In this chapter, we'll provide a high-level overview of traversal, we'll +explain the concept of a resource tree, and we'll show how traversal might be +used within an application. .. index:: single: traversal analogy @@ -55,16 +55,16 @@ can run the ``cat`` command: The contents of ``myfile`` are now printed on the user's behalf. -:app:`Pyramid` is very much like this inexperienced UNIX user as it -uses :term:`traversal` against an object graph. In this analogy, we -can map the ``cat`` program to the :app:`Pyramid` concept of a -:term:`view callable`: it is a program that can be run against some -:term:`context` as the result of :term:`view lookup`. The file being -operated on in this analogy is the :term:`context` object; the context -is the "last node found" in a traversal. The directory structure is -the object graph being traversed. The act of progressively changing -directories to find the file as well as the handling of a ``cd`` error -as a stop condition is analogous to :term:`traversal`. +:app:`Pyramid` is very much like this inexperienced UNIX user as it uses +:term:`traversal` against a resource tree. In this analogy, we can map the +``cat`` program to the :app:`Pyramid` concept of a :term:`view callable`: it +is a program that can be run against some :term:`context` as the result of +:term:`view lookup`. The file being operated on in this analogy is the +:term:`context` object; the context is the "last resource found" in a +traversal. The directory structure is the resource tree being traversed. +The act of progressively changing directories to find the file as well as the +handling of a ``cd`` error as a stop condition is analogous to +:term:`traversal`. The analogy we've used is not *exactly* correct, because, while the naive user already knows which command he wants to invoke before he @@ -94,9 +94,9 @@ Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of path segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is converted to the sequence ``['a', 'b', 'c']``. -After the path info is converted, a lookup is performed against the -object graph for each path segment. Each lookup uses the -``__getitem__`` method of an object in the graph. +After the path info is converted, a lookup is performed against the resource +tree for each path segment. Each lookup uses the ``__getitem__`` method of +an object in the tree. For example, if the path info sequence is ``['a', 'b', 'c']``: @@ -121,13 +121,13 @@ This process continues until the path segment sequence is exhausted or a lookup for a path element fails. In either case, a :term:`context` is found. -Traversal "stops" when it either reaches a leaf level model instance -in your object graph or when the path segments implied by the URL "run -out". The object that traversal "stops on" becomes the -:term:`context`. If at any point during traversal any node in the -graph doesn't have a ``__getitem__`` method, or if the ``__getitem__`` -method of a node raises a :exc:`KeyError`, traversal ends immediately, -and that node becomes the :term:`context`. +Traversal "stops" when it either reaches a leaf level resource in your +resource tree or when the path segments implied by the URL "run out". The +object that traversal "stops on" becomes the :term:`context`. If at any +point during traversal any resource in the tree doesn't have a +``__getitem__`` method, or if the ``__getitem__`` method of a resource raises +a :exc:`KeyError`, traversal ends immediately, and that resource becomes the +:term:`context`. The results of a :term:`traversal` also include a :term:`view name`. The :term:`view name` is the *first* URL path segment in the set of @@ -142,18 +142,18 @@ request. How :app:`Pyramid` performs view lookup is explained within the :ref:`views_chapter` chapter. .. index:: - single: object graph - single: traversal graph - single: model graph + single: object tree + single: traversal tree + single: resource tree -.. _the_object_graph: +.. _the_resource_tree: -The Object Graph ----------------- +The Resource Tree +----------------- -When your application uses :term:`traversal` to resolve URLs to code, -your application must supply an *object graph* to :app:`Pyramid`. -This graph is represented by a :term:`root` object. +When your application uses :term:`traversal` to resolve URLs to code, your +application must supply the a resource tree to :app:`Pyramid`. This tree is +represented by a :term:`root` object. In order to supply a root object for an application, at system startup time, the :app:`Pyramid` :term:`Router` is configured with a @@ -188,19 +188,18 @@ alternately be passed to the ``Configurator`` as a :term:`dotted Python name` which refers to a root factory object defined in a different module. -A root factory is passed a :term:`request` object and it is expected -to return an object which represents the root of the object graph. -All :term:`traversal` will begin at this root object. Usually a root -factory for a traversal-based application will be more complicated -than the above ``Root`` object; in particular it may be associated -with a database connection or another persistence mechanism. A root -object is often an instance of a class which has a ``__getitem__`` -method. +A root factory is passed a :term:`request` object and it is expected to +return an object which represents the root of the resource tree. All +:term:`traversal` will begin at this root object. Usually a root factory for +a traversal-based application will be more complicated than the above +``Root`` object; in particular it may be associated with a database +connection or another persistence mechanism. A root object is often an +instance of a class which has a ``__getitem__`` method. If no :term:`root factory` is passed to the :app:`Pyramid` :term:`Configurator` constructor, or the ``root_factory`` is specified as the value ``None``, a *default* root factory is used. The default -root factory always returns an object that has no child nodes. +root factory always returns an object that has no child resources. .. sidebar:: Emulating the Default Root Factory @@ -218,47 +217,48 @@ root factory always returns an object that has no child nodes. config = Configurator(root_factory=Root) The default root factory is just a really stupid object that has no - behavior or state. Using :term:`traversal` against an application - that uses the object graph supplied by the default root object is - not very interesting, because the default root object has no - children. Its availability is more useful when you're developing - an application using :term:`URL dispatch`. - -Items contained within the object graph are sometimes analogous to the -concept of :term:`model` objects used by many other frameworks (and -:app:`Pyramid` APIs often refers to them as "models", as well). -They are typically instances of Python classes. - -The object graph consists of *container* nodes and *leaf* nodes. -There is only one difference between a *container* node and a *leaf* -node: *container* nodes possess a ``__getitem__`` method while *leaf* -nodes do not. The ``__getitem__`` method was chosen as the signifying -difference between the two types of nodes because the presence of this + behavior or state. Using :term:`traversal` against an application that + uses the resource tree supplied by the default root object is not very + interesting, because the default root object has no children. Its + availability is more useful when you're developing an application using + :term:`URL dispatch`. + +.. note:: + + 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 @@ -273,7 +273,7 @@ This section will attempt to explain the :app:`Pyramid` traversal algorithm. We'll provide a description of the algorithm, a diagram of how the algorithm works, and some example traversal scenarios that might help you understand how the algorithm operates against a -specific object graph. +specific resource tree. We'll also talk a bit about :term:`view lookup`. The :ref:`views_chapter` chapter discusses :term:`view lookup` in detail, @@ -314,17 +314,15 @@ and a :term:`view name`. stripped off ``PATH_INFO``, and the remaining path segments are split on the slash character to form a traversal sequence. - The traversal algorithm by default attempts to first URL-unquote - and then Unicode-decode each path segment derived from - ``PATH_INFO`` from its natural byte string (``str`` type) - representation. URL unquoting is performed using the Python - standard library ``urllib.unquote`` function. Conversion from a - URL-decoded string into Unicode is attempted using the UTF-8 - encoding. If any URL-unquoted path segment in ``PATH_INFO`` is - not decodeable using the UTF-8 decoding, a :exc:`TypeError` is - raised. A segment will be fully URL-unquoted and UTF8-decoded - before it is passed it to the ``__getitem__`` of any model object - during traversal. + The traversal algorithm by default attempts to first URL-unquote and then + Unicode-decode each path segment derived from ``PATH_INFO`` from its + natural byte string (``str`` type) representation. URL unquoting is + performed using the Python standard library ``urllib.unquote`` function. + Conversion from a URL-decoded string into Unicode is attempted using the + UTF-8 encoding. If any URL-unquoted path segment in ``PATH_INFO`` is not + decodeable using the UTF-8 decoding, a :exc:`TypeError` is raised. A + segment will be fully URL-unquoted and UTF8-decoded before it is passed + it to the ``__getitem__`` of any resource during traversal. Thus, a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the traversal sequence ``[u'a', u'b', u'c']``. @@ -340,12 +338,11 @@ and a :term:`view name`. for the name ``c``, and may return "object ``c``". #. Traversal ends when a) the entire path is exhausted or b) when any - graph element raises a :exc:`KeyError` from its ``__getitem__`` or - c) when any non-final path element traversal does not have a - ``__getitem__`` method (resulting in a :exc:`NameError`) or d) - when any path element is prefixed with the set of characters - ``@@`` (indicating that the characters following the ``@@`` token - should be treated as a :term:`view name`). + resouce raises a :exc:`KeyError` from its ``__getitem__`` or c) when any + non-final path element traversal does not have a ``__getitem__`` method + (resulting in a :exc:`NameError`) or d) when any path element is prefixed + with the set of characters ``@@`` (indicating that the characters + following the ``@@`` token should be treated as a :term:`view name`). #. When traversal ends for any of the reasons in the previous step, the last object found during traversal is deemed to be the @@ -375,19 +372,18 @@ The traversal algorithm exposes two special cases: The default view is a view that is registered with no name or a view which is registered with a name that equals the empty string. -- If any path segment element begins with the special characters - ``@@`` (think of them as goggles), the value of that segment minus - the goggle characters is considered the :term:`view name` - immediately and traversal stops there. This allows you to address - views that may have the same names as model instance names in the - graph unambiguously. +- If any path segment element begins with the special characters ``@@`` + (think of them as goggles), the value of that segment minus the goggle + characters is considered the :term:`view name` immediately and traversal + stops there. This allows you to address views that may have the same names + as resource names in the tree unambiguously. Finally, traversal is responsible for locating a :term:`virtual root`. A virtual root is used during "virtual hosting"; see the :ref:`vhosting_chapter` chapter for information. We won't speak more about it in this chapter. -.. image:: modelgraphtraverser.png +.. image:: resourcetreetraverser.png .. index:: single: traversal examples @@ -397,13 +393,13 @@ Traversal Algorithm Examples No one can be expected to understand the traversal algorithm by analogy and description alone, so let's examine some traversal -scenarios that use concrete URLs and object graph compositions. +scenarios that use concrete URLs and resource tree compositions. Let's pretend the user asks for ``http://example.com/foo/bar/baz/biz/buz.txt``. The request's ``PATH_INFO`` in that case is ``/foo/bar/baz/biz/buz.txt``. Let's further pretend that when this request comes in that we're traversing -the following object graph: +the following resource tree: .. code-block:: text @@ -450,7 +446,7 @@ Let's say that view lookup finds no matching view type. In this circumstance, the :app:`Pyramid` :term:`router` returns the result of the :term:`not found view` and the request ends. -However, for this graph: +However, for this tree: .. code-block:: text @@ -537,6 +533,6 @@ The :mod:`pyramid.traversal` module contains API functions that deal with traversal, such as traversal invocation from within application code. -The :func:`pyramid.url.model_url` function generates a URL when -given an object retrieved from an object graph. +The :func:`pyramid.url.resource_url` function generates a URL when +given an object retrieved from an resource tree. diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index 3c31f69b6..6a721f403 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -12,23 +12,22 @@ your application. In this context, a "unit" is often a function or a method of a class instance. The unit is also referred to as a "unit under test". -The goal of a single unit test is to test **only** some permutation of -the "unit under test". If you write a unit test that aims to verify -the result of a particular codepath through a Python function, you -need only be concerned about testing the code that *lives in the -function body itself*. If the function accepts a parameter that -represents a complex application "domain object" (such as a model, a -database connection, or an SMTP server), the argument provided to this -function during a unit test *need not be* and likely *should not be* a -"real" implementation object. For example, although a particular -function implementation may accept an argument that represents an SMTP -server object, and the function may call a method of this object when -the system is operating normally that would result in an email being -sent, a unit test of this codepath of the function does *not* need to -test that an email is actually sent. It just needs to make sure that -the function calls the method of the object provided as an argument -that *would* send an email if the argument happened to be the "real" -implementation of an SMTP server object. +The goal of a single unit test is to test **only** some permutation of the +"unit under test". If you write a unit test that aims to verify the result +of a particular codepath through a Python function, you need only be +concerned about testing the code that *lives in the function body itself*. +If the function accepts a parameter that represents a complex application +"domain object" (such as a resource, a database connection, or an SMTP +server), the argument provided to this function during a unit test *need not +be* and likely *should not be* a "real" implementation object. For example, +although a particular function implementation may accept an argument that +represents an SMTP server object, and the function may call a method of this +object when the system is operating normally that would result in an email +being sent, a unit test of this codepath of the function does *not* need to +test that an email is actually sent. It just needs to make sure that the +function calls the method of the object provided as an argument that *would* +send an email if the argument happened to be the "real" implementation of an +SMTP server object. An *integration test*, on the other hand, is a different form of testing in which the interaction between two or more "units" is @@ -298,9 +297,9 @@ sure to use this pattern in your test case's ``setUp`` and See the :ref:`testing_module` chapter for the entire :app:`Pyramid` -specific testing API. This chapter describes APIs for registering a -security policy, registering models at paths, registering event +security policy, registering resources at paths, registering event listeners, registering views and view permissions, and classes -representing "dummy" implementations of a request and a model. +representing "dummy" implementations of a request and a resource. See also the various methods of the :term:`Configurator` documented in :ref:`configuration_module` that begin with the ``testing_`` prefix. diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index f0ace76ba..739a826c1 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -375,7 +375,7 @@ order of route configuration declarations is very important. The order that routes declarations are evaluated is the order in which they are added to the application at startup time. This is unlike :term:`traversal`, which depends on emergent behavior which happens as -a result of traversing a graph. +a result of traversing a resource tree. For routes added via the :mod:`pyramid.config.Configurator.add_route` method, the order that routes are evaluated is the order in which they are @@ -414,7 +414,7 @@ found via :term:`view lookup`. :linenos: config.add_route('abc', '/abc', view='myproject.views.theview', - factory='myproject.models.root_factory') + factory='myproject.resources.root_factory') The factory can either be a Python object or a :term:`dotted Python name` (a string) which points to such a Python object, as it is above. @@ -459,7 +459,7 @@ represent neither predicates nor view configuration information. A Python object (often a function or a class) or a :term:`dotted Python name` to such an object that will generate a :app:`Pyramid` :term:`context` object when this route - matches. For example, ``mypackage.models.MyFactoryClass``. If this + matches. For example, ``mypackage.resources.MyFactoryClass``. If this argument is not specified, the traversal root factory will be used. ``traverse`` @@ -613,7 +613,7 @@ represent neither predicates nor view configuration information. ``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 @@ -979,10 +979,10 @@ An example of using a route with a factory: config.add_route('idea', 'ideas/{idea}', view='myproject.views.idea_view', - factory='myproject.models.Idea') + factory='myproject.resources.Idea') -The above route will manufacture an ``Idea`` model as a -:term:`context`, assuming that ``mypackage.models.Idea`` resolves to a +The above route will manufacture an ``Idea`` resource as a +:term:`context`, assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a request in its ``__init__``. For example: .. code-block:: python diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index d1fd1b382..65168806e 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -85,21 +85,19 @@ Virtual Root Support :term:`traversal` -based (but not :term:`URL dispatch` -based) applications. -Virtual root support is useful when you'd like to host some model in a -:app:`Pyramid` object graph as an application under a URL pathname -that does not include the model path itself. For example, you might -want to serve the object at the traversal path ``/cms`` as an -application reachable via ``http://example.com/`` (as opposed to -``http://example.com/cms``). - -To specify a virtual root, cause an environment variable to be -inserted into the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value -that is the absolute pathname to the model object in the traversal -graph that should behave as the "root" model. As a result, the -traversal machinery will respect this value during traversal -(prepending it to the PATH_INFO before traversal starts), and the -:func:`pyramid.url.model_url` API will generate the "correct" -virtually-rooted URLs. +Virtual root support is useful when you'd like to host some resource in a +:app:`Pyramid` resource tree as an application under a URL pathname that does +not include the resource path itself. For example, you might want to serve the +object at the traversal path ``/cms`` as an application reachable via +``http://example.com/`` (as opposed to ``http://example.com/cms``). + +To specify a virtual root, cause an environment variable to be inserted into +the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value that is the absolute +pathname to the resource object in the resource tree that should behave as +the "root" resource. As a result, the traversal machinery will respect this +value during traversal (prepending it to the PATH_INFO before traversal +starts), and the :func:`pyramid.url.resource_url` API will generate the +"correct" virtually-rooted URLs. An example of an Apache ``mod_proxy`` configuration that will host the ``/cms`` subobject as ``http://www.example.com/`` using this facility diff --git a/docs/narr/views.rst b/docs/narr/views.rst index ce2884599..cda6de48b 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -3,11 +3,10 @@ Views ===== -The primary job of any :app:`Pyramid` application is is to find and -invoke a :term:`view callable` when a :term:`request` reaches the -application. View callables are bits of code written by you -- the -application developer -- which do something interesting in response to -a request made to your application. +The primary job of any :app:`Pyramid` application is is to find and invoke a +:term:`view callable` when a :term:`request` reaches the application. View +callables are bits of code which do something interesting in response to a +request made to your application. .. note:: @@ -18,11 +17,10 @@ a request made to your application. that implements a view *callable*, and the process of view *lookup*. -The chapter :ref:`contextfinding_chapter` describes how, using -information from the :term:`request`, a :term:`context` and a -:term:`view name` are computed. But neither the context nor the view -name found are very useful unless those elements can eventually be -mapped to a :term:`view callable`. +The chapter :ref:`contextfinding_chapter` describes how, using information +from the :term:`request`, a :term:`context` and a :term:`view name` are +computed. But neither the context nor the view name found are very useful +unless those elements can eventually be mapped to a :term:`view callable`. The job of actually locating and invoking the "best" :term:`view callable` is the job of the :term:`view lookup` subsystem. The view @@ -139,9 +137,8 @@ represent the method expected to return a response, you can use an function defined in this style can be defined as follows: context - An instance of a :term:`context` found via graph :term:`traversal` - or :term:`URL dispatch`. If the context is found via traversal, it - will be a :term:`model` object. + The :term:`resource` object found via tree :term:`traversal` + or :term:`URL dispatch`. request A :app:`Pyramid` Request object representing the current WSGI @@ -476,7 +473,7 @@ You can configure a view to use the JSON renderer by naming ``json`` as the config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='json') @@ -494,22 +491,21 @@ attributes by attaching properties to the request. See Two built-in renderers exist for :term:`Chameleon` templates. -If the ``renderer`` attribute of a view configuration is an absolute -path, a relative path or :term:`resource specification` which has a -final path element with a filename extension of ``.pt``, the Chameleon -ZPT renderer is used. See :ref:`chameleon_zpt_templates` for more -information about ZPT templates. +If the ``renderer`` attribute of a view configuration is an absolute path, a +relative path or :term:`asset specification` which has a final path element +with a filename extension of ``.pt``, the Chameleon ZPT renderer is used. +See :ref:`chameleon_zpt_templates` for more information about ZPT templates. If the ``renderer`` attribute of a view configuration is an absolute path or -a :term:`resource specification` which has a final path element with a -filename extension of ``.txt``, the :term:`Chameleon` text renderer is used. -See :ref:`chameleon_zpt_templates` for more information about Chameleon text +a :term:`asset specification` which has a final path element with a filename +extension of ``.txt``, the :term:`Chameleon` text renderer is used. See +:ref:`chameleon_zpt_templates` for more information about Chameleon text templates. The behavior of these renderers is the same, except for the engine used to render the template. -When a ``renderer`` attribute that names a template path or :term:`resource +When a ``renderer`` attribute that names a template path or :term:`asset specification` (e.g. ``myproject:templates/foo.pt`` or ``myproject:templates/foo.txt``) is used, the view must return a :term:`Response` object or a Python *dictionary*. If the view callable with @@ -541,7 +537,7 @@ renderer: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='myproject:templates/foo.pt') Here's an example view configuration which uses a Chameleon text @@ -552,7 +548,7 @@ renderer: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='myproject:templates/foo.txt') Views which use a Chameleon renderer can vary response attributes by @@ -573,13 +569,13 @@ The dictionary items will then be used in the global template space. If the view callable returns anything but a Response object, or a dictionary, an error will be raised. -When using a ``renderer`` argument to a :term:`view configuration` to -specify a Mako template, the value of the ``renderer`` may be a path -relative to the ``mako.directories`` setting (e.g. -``some/template.mak``) or, alternately, it may be a :term:`resource -specification` (e.g. ``apackage:templates/sometemplate.mak``). Mako -templates may internally inherit other Mako templates using a relative -filename or a :term:`resource specification` as desired. +When using a ``renderer`` argument to a :term:`view configuration` to specify +a Mako template, the value of the ``renderer`` may be a path relative to the +``mako.directories`` setting (e.g. ``some/template.mak``) or, alternately, +it may be a :term:`asset specification` +(e.g. ``apackage:templates/sometemplate.mak``). Mako templates may +internally inherit other Mako templates using a relative filename or a +:term:`asset specification` as desired. XXX Further explanation or link to mako inheritance info @@ -592,7 +588,7 @@ Here's an example view configuration which uses a relative path: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='foo.mak') It's important to note that in Mako's case, the 'relative' path name @@ -600,16 +596,15 @@ It's important to note that in Mako's case, the 'relative' path name directory (or directories) configured for Mako via the ``mako.directories`` configuration file setting. -The renderer can also be provided in :term:`resource specification` -format. Here's an example view configuration which uses a :term:`resource -specification`: +The renderer can also be provided in :term:`asset specification` +format. Here's an example view configuration which uses one: .. code-block:: python :linenos: config.add_view('myproject.views.hello_world', name='hello', - context='myproject.models.Hello', + context='myproject.resources.Hello', renderer='mypackage:templates/foo.mak') The above configuration will use the file named ``foo.mak`` in the @@ -746,26 +741,27 @@ factory constructor is available as :class:`pyramid.interfaces.IRendererInfo`. There are essentially two different kinds of renderer factories: -- A renderer factory which expects to accept a :term:`resource specification`, - or an absolute path, as the ``name`` attribute of the ``info`` object fed to - its constructor. These renderer factories are registered with a ``name`` - value that begins with a dot (``.``). These types of renderer factories - usually relate to a file on the filesystem, such as a template. - -- A renderer factory which expects to accept a token that does not represent a - filesystem path or a resource specification in the ``name`` attribute of the +- A renderer factory which expects to accept a :term:`asset + specification`, or an absolute path, as the ``name`` attribute of the ``info`` object fed to its constructor. These renderer factories are - registered with a ``name`` value that does not begin with a dot. These - renderer factories are typically object serializers. + registered with a ``name`` value that begins with a dot (``.``). These + types of renderer factories usually relate to a file on the filesystem, + such as a template. + +- A renderer factory which expects to accept a token that does not represent + a filesystem path or a asset specification in the ``name`` + attribute of the ``info`` object fed to its constructor. These renderer + factories are registered with a ``name`` value that does not begin with a + dot. These renderer factories are typically object serializers. -.. sidebar:: Resource Specifications +.. sidebar:: Asset Specifications - A resource specification is a colon-delimited identifier for a - :term:`resource`. The colon separates a Python :term:`package` - name from a package subpath. For example, the resource - specification ``my.package:static/baz.css`` identifies the file - named ``baz.css`` in the ``static`` subdirectory of the - ``my.package`` Python :term:`package`. + A asset specification is a colon-delimited identifier for a + :term:`asset`. The colon separates a Python :term:`package` + name from a package subpath. For example, the asset + specification ``my.package:static/baz.css`` identifies the file named + ``baz.css`` in the ``static`` subdirectory of the ``my.package`` Python + :term:`package`. Here's an example of the registration of a simple renderer factory via :meth:`pyramid.config.Configurator.add_renderer`: @@ -1211,30 +1207,26 @@ Non-Predicate Arguments itself if the view is a function, or the ``__call__`` callable attribute if the view is a class). -``renderer`` - This is either a single string term (e.g. ``json``) or a string - implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``) naming a :term:`renderer` - implementation. If the ``renderer`` value does not contain a dot - (``.``), the specified string will be used to look up a renderer - implementation, and that renderer implementation will be used to - construct a response from the view return value. If the - ``renderer`` value contains a dot (``.``), the specified term will - be treated as a path, and the filename extension of the last element - in the path will be used to look up the renderer implementation, - which will be passed the full path. The renderer implementation - will be used to construct a :term:`response` from the view return - value. - - When the renderer is a path, although a path is usually just a - simple relative pathname (e.g. ``templates/foo.pt``, implying that a - template named "foo.pt" is in the "templates" directory relative to - the directory of the current :term:`package`), a path can be - absolute, starting with a slash on UNIX or a drive letter prefix on - Windows. The path can alternately be a :term:`resource - specification` in the form - ``some.dotted.package_name:relative/path``, making it possible to - address template resources which live in a separate package. +``renderer`` This is either a single string term (e.g. ``json``) or a string + implying a path or :term:`asset specification` + (e.g. ``templates/views.pt``) naming a :term:`renderer` implementation. If + the ``renderer`` value does not contain a dot (``.``), the specified string + will be used to look up a renderer implementation, and that renderer + implementation will be used to construct a response from the view return + value. If the ``renderer`` value contains a dot (``.``), the specified + term will be treated as a path, and the filename extension of the last + element in the path will be used to look up the renderer implementation, + which will be passed the full path. The renderer implementation will be + used to construct a :term:`response` from the view return value. + + When the renderer is a path, although a path is usually just a simple + relative pathname (e.g. ``templates/foo.pt``, implying that a template + named "foo.pt" is in the "templates" directory relative to the directory of + the current :term:`package`), a path can be absolute, starting with a slash + on UNIX or a drive letter prefix on Windows. The path can alternately be a + :term:`asset specification` in the form + ``some.dotted.package_name:relative/path``, making it possible to address + template assets which live in a separate package. The ``renderer`` attribute is optional. If it is not defined, the "null" renderer is assumed (no rendering is performed and the value @@ -1276,15 +1268,15 @@ the usage of the configured view. default view). ``context`` - An object representing a Python class that the :term:`context` must be - an instance of, *or* the :term:`interface` that the :term:`context` - must provide in order for this view to be found and called. This - predicate is true when the :term:`context` is an instance of the - represented class or if the :term:`context` provides the represented - interface; it is otherwise false. + An object representing a Python class that the :term:`context` resource + must be an instance of, *or* the :term:`interface` that the :term:`context` + must provide in order for this view to be found and called. This predicate + is true when the :term:`context` is an instance of the represented class or + if the :term:`context` provides the represented interface; it is otherwise + false. - If ``context`` is not supplied, the value ``None``, which matches - any model, is used. + If ``context`` is not supplied, the value ``None``, which matches any + resource, is used. ``route_name`` If ``route_name`` is supplied, the view callable will be invoked @@ -1344,7 +1336,7 @@ the usage of the configured view. This value should be a reference to a Python class or :term:`interface` that a parent object in the context's :term:`lineage` must provide in order for this view to be found and - called. The nodes in your object graph must be "location-aware" to + called. The resources in your resource tree must be "location-aware" to use this feature. If ``containment`` is not supplied, the interfaces and classes in @@ -1469,11 +1461,11 @@ reside in a :app:`Pyramid` application module ``views.py``: .. code-block:: python :linenos: - from models import MyModel + from resources import MyResource from pyramid.view import view_config from pyramid.chameleon_zpt import render_template_to_response - @view_config(name='my_view', request_method='POST', context=MyModel, + @view_config(name='my_view', request_method='POST', context=MyResource, permission='read', renderer='templates/my.pt') def my_view(request): return {'a':1} @@ -1486,7 +1478,7 @@ configuration stanza: :linenos: config.add_view('.views.my_view', name='my_view', request_method='POST', - context=MyModel, permission='read') + context=MyResource, permission='read') All arguments to ``view_config`` may be omitted. For example: @@ -1503,7 +1495,7 @@ All arguments to ``view_config`` may be omitted. For example: Such a registration as the one directly above implies that the view name will be ``my_view``, registered with a ``context`` argument that -matches any model type, using no permission, registered against +matches any resource type, using no permission, registered against requests with any request method, request type, request param, route name, or containment. @@ -1686,32 +1678,32 @@ such an object. All other arguments are optional. See information. .. index:: - single: model interfaces + single: resource interfaces -.. _using_model_interfaces: +.. _using_resource_interfaces: -Using Model Interfaces In View Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using Resource Interfaces In View Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of registering your views with a ``context`` that names a -Python model *class*, you can optionally register a view callable with -a ``context`` which is an :term:`interface`. An interface can be -attached arbitrarily to any model instance. View lookup treats -context interfaces specially, and therefore the identity of a model -can be divorced from that of the class which implements it. As a -result, associating a view with an interface can provide more -flexibility for sharing a single view between two or more different -implementations of a model type. For example, if two model object -instances of different Python class types share the same interface, -you can use the same view against each of them. +Instead of registering your views with a ``context`` that names a Python +resource *class*, you can optionally register a view callable with a +``context`` which is an :term:`interface`. An interface can be attached +arbitrarily to any resource object. View lookup treats context interfaces +specially, and therefore the identity of a resource can be divorced from that +of the class which implements it. As a result, associating a view with an +interface can provide more flexibility for sharing a single view between two +or more different implementations of a resource type. For example, if two +resource objects of different Python class types share the same interface, +you can use the same view configuration to specify both of them as a +``context``. -In order to make use of interfaces in your application during view -dispatch, you must create an interface and mark up your model classes -or instances with interface declarations that refer to this interface. +In order to make use of interfaces in your application during view dispatch, +you must create an interface and mark up your resource classes or instances +with interface declarations that refer to this interface. -To attach an interface to a model *class*, you define the interface -and use the :func:`zope.interface.implements` function to associate -the interface with the class. +To attach an interface to a resource *class*, you define the interface and +use the :func:`zope.interface.implements` function to associate the interface +with the class. .. code-block:: python :linenos: @@ -1725,10 +1717,10 @@ the interface with the class. class Hello(object): implements(IHello) -To attach an interface to a model *instance*, you define the interface -and use the :func:`zope.interface.alsoProvides` function to associate -the interface with the instance. This function mutates the instance -in such a way that the interface is attached to it. +To attach an interface to a resource *instance*, you define the interface and +use the :func:`zope.interface.alsoProvides` function to associate the +interface with the instance. This function mutates the instance in such a +way that the interface is attached to it. .. code-block:: python :linenos: @@ -1747,13 +1739,13 @@ in such a way that the interface is attached to it. alsoProvides(hello, IHello) return hello -Regardless of how you associate an interface, with a model instance, or a model -class, the resulting code to associate that interface with a view callable is -the same. Assuming the above code that defines an ``IHello`` interface lives -in the root of your application, and its module is named "models.py", the -interface declaration below will associate the -``mypackage.views.hello_world`` view with models that implement, or provide, -this interface. +Regardless of how you associate an interface, with a resource instance, or a +resource class, the resulting code to associate that interface with a view +callable is the same. Assuming the above code that defines an ``IHello`` +interface lives in the root of your application, and its module is named +"resources.py", the interface declaration below will associate the +``mypackage.views.hello_world`` view with resources that implement, or +provide, this interface. .. code-block:: python :linenos: @@ -1761,22 +1753,25 @@ this interface. # config is an instance of pyramid.config.Configurator config.add_view('mypackage.views.hello_world', name='hello.html', - context='mypackage.models.IHello') + context='mypackage.resources.IHello') -Any time a model that is determined to be the :term:`context` provides this -interface, and a view named ``hello.html`` is looked up against it as per the -URL, the ``mypackage.views.hello_world`` view callable will be invoked. +Any time a resource that is determined to be the :term:`context` provides +this interface, and a view named ``hello.html`` is looked up against it as +per the URL, the ``mypackage.views.hello_world`` view callable will be +invoked. -Note, in cases where a view is registered against a model class, and a -view is also registered against an interface that the model class -implements, an ambiguity arises. Views registered for the model class -take precedence over any views registered for any interface the model -class implements. Thus, if a view is registered for both the class type -of the context and an interface implemented by the context's class, the -view registered for the context's class will "win". +Note, in cases where a view is registered against a resource class, and a +view is also registered against an interface that the resource class +implements, an ambiguity arises. Views registered for the resource class take +precedence over any views registered for any interface the resource class +implements. Thus, if one view configuration names a ``context`` of both the +class type of a resource, and another view configuration names a ``context`` +of interface implemented by the resource's class, and both view +configurations are otherwise identical, the view registered for the context's +class will "win". -For more information about defining models with interfaces for use within -view configuration, see :ref:`models_which_implement_interfaces`. +For more information about defining resources with interfaces for use within +view configuration, see :ref:`resources_which_implement_interfaces`. .. index:: single: view security @@ -1800,7 +1795,7 @@ using :meth:`pyramid.config.Configurator.add_view`: # config is an instance of pyramid.config.Configurator config.add_view('myproject.views.add_entry', name='add.html', - context='myproject.models.IBlog', permission='add') + context='myproject.resources.IBlog', permission='add') When an :term:`authorization policy` is enabled, this view will be protected with the ``add`` permission. The view will *not be called* if diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst index c930ecc50..f330fd551 100644 --- a/docs/narr/zca.rst +++ b/docs/narr/zca.rst @@ -129,7 +129,7 @@ interface of a registry instead, you need only know how to obtain the There are two ways of doing so: - use the :func:`pyramid.threadlocal.get_current_registry` - function within :app:`Pyramid` view or model code. This will + function within :app:`Pyramid` view or resource code. This will always return the "current" :app:`Pyramid` application registry. - use the attribute of the :term:`request` object named ``registry`` diff --git a/docs/tutorials/catalog/index.rst b/docs/tutorials/catalog/index.rst index 43b078edc..e4e5bd720 100644 --- a/docs/tutorials/catalog/index.rst +++ b/docs/tutorials/catalog/index.rst @@ -88,11 +88,11 @@ want the application to be based on :term:`traversal`. 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() diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index 3dbf10bd8..c7c722f70 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -98,11 +98,15 @@ following: 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``: diff --git a/docs/tutorials/wiki/definingmodels.rst b/docs/tutorials/wiki/definingmodels.rst index 097485047..f317d31dd 100644 --- a/docs/tutorials/wiki/definingmodels.rst +++ b/docs/tutorials/wiki/definingmodels.rst @@ -1,15 +1,17 @@ -=============== -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/ diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst index 97314fb77..5250cb5e5 100644 --- a/docs/tutorials/wiki/definingviews.rst +++ b/docs/tutorials/wiki/definingviews.rst @@ -51,15 +51,15 @@ wiki itself (it will answer on the root URL), another named 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 ------------------------------- diff --git a/docs/tutorials/wiki/src/authorization/tutorial/login.py b/docs/tutorials/wiki/src/authorization/tutorial/login.py index 60e69fddf..a1194feb0 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/login.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/login.py @@ -1,7 +1,7 @@ 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 @@ -11,7 +11,7 @@ from tutorial.security import USERS @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 @@ -39,6 +39,6 @@ def login(request): @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) diff --git a/docs/tutorials/wiki/src/authorization/tutorial/tests.py b/docs/tutorials/wiki/src/authorization/tutorial/tests.py index d082fb84d..aaf753816 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/tests.py @@ -43,7 +43,7 @@ class AppmakerTests(unittest.TestCase): 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') @@ -54,9 +54,9 @@ class ViewPageTests(unittest.TestCase): 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() @@ -80,17 +80,18 @@ class AddPageTests(unittest.TestCase): 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'] @@ -106,16 +107,16 @@ class EditPageTests(unittest.TestCase): 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) diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py index 48e4e2b43..3143ab552 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py @@ -2,7 +2,7 @@ from docutils.core import publish_parts 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 @@ -16,7 +16,7 @@ wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @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): @@ -26,7 +26,7 @@ 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 @@ -34,7 +34,7 @@ def view_page(context, request): 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) @@ -51,8 +51,8 @@ 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 @@ -66,11 +66,11 @@ def add_page(context, request): 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) diff --git a/docs/tutorials/wiki/src/viewdecorators/tutorial/tests.py b/docs/tutorials/wiki/src/viewdecorators/tutorial/tests.py index d082fb84d..aaf753816 100644 --- a/docs/tutorials/wiki/src/viewdecorators/tutorial/tests.py +++ b/docs/tutorials/wiki/src/viewdecorators/tutorial/tests.py @@ -43,7 +43,7 @@ class AppmakerTests(unittest.TestCase): 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') @@ -54,9 +54,9 @@ class ViewPageTests(unittest.TestCase): 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() @@ -80,17 +80,18 @@ class AddPageTests(unittest.TestCase): 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'] @@ -106,16 +107,16 @@ class EditPageTests(unittest.TestCase): 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) diff --git a/docs/tutorials/wiki/src/viewdecorators/tutorial/views.py b/docs/tutorials/wiki/src/viewdecorators/tutorial/views.py index 168965db2..c8ac46edf 100644 --- a/docs/tutorials/wiki/src/viewdecorators/tutorial/views.py +++ b/docs/tutorials/wiki/src/viewdecorators/tutorial/views.py @@ -2,7 +2,7 @@ from docutils.core import publish_parts 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 @@ -13,7 +13,7 @@ wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @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): @@ -23,7 +23,7 @@ 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 @@ -31,7 +31,7 @@ def view_page(context, request): 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') @@ -43,8 +43,8 @@ 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 @@ -54,9 +54,9 @@ def add_page(context, request): 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')) diff --git a/docs/tutorials/wiki/src/views/tutorial/tests.py b/docs/tutorials/wiki/src/views/tutorial/tests.py index c5aafcac5..28e424884 100644 --- a/docs/tutorials/wiki/src/views/tutorial/tests.py +++ b/docs/tutorials/wiki/src/views/tutorial/tests.py @@ -43,7 +43,7 @@ class AppmakerTests(unittest.TestCase): 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') @@ -54,9 +54,9 @@ class ViewPageTests(unittest.TestCase): 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() @@ -80,18 +80,18 @@ class AddPageTests(unittest.TestCase): 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'] @@ -107,16 +107,16 @@ class EditPageTests(unittest.TestCase): 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) diff --git a/docs/tutorials/wiki/src/views/tutorial/views.py b/docs/tutorials/wiki/src/views/tutorial/views.py index acc1bbb57..8437fdc51 100644 --- a/docs/tutorials/wiki/src/views/tutorial/views.py +++ b/docs/tutorials/wiki/src/views/tutorial/views.py @@ -2,7 +2,7 @@ from docutils.core import publish_parts import re from pyramid.httpexceptions import HTTPFound -from pyramid.url import model_url +from pyramid.url import resource_url from tutorial.models import Page @@ -10,7 +10,7 @@ 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__ @@ -19,7 +19,7 @@ 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 @@ -27,7 +27,7 @@ def view_page(context, request): 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): @@ -38,8 +38,8 @@ 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 @@ -48,9 +48,9 @@ def add_page(context, request): 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')) diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index b710e1396..09e1f26c3 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -1,10 +1,10 @@ -=============== -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/ diff --git a/docs/zcml.rst b/docs/zcml.rst index 5913f9460..e5bbe5d4b 100644 --- a/docs/zcml.rst +++ b/docs/zcml.rst @@ -13,6 +13,7 @@ documentation is organized alphabetically by directive name. zcml/aclauthorizationpolicy zcml/adapter zcml/authtktauthenticationpolicy + zcml/asset zcml/configure zcml/default_permission zcml/forbidden @@ -23,7 +24,6 @@ documentation is organized alphabetically by directive name. zcml/remoteuserauthenticationpolicy zcml/renderer zcml/repozewho1authenticationpolicy - zcml/resource zcml/route zcml/scan zcml/static diff --git a/docs/zcml/aclauthorizationpolicy.rst b/docs/zcml/aclauthorizationpolicy.rst index 2c66da0c8..f09531415 100644 --- a/docs/zcml/aclauthorizationpolicy.rst +++ b/docs/zcml/aclauthorizationpolicy.rst @@ -4,7 +4,7 @@ -------------------------- 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 ~~~~~~~~~~ diff --git a/docs/zcml/resource.rst b/docs/zcml/asset.rst index 3f7c58faa..af7a6db94 100644 --- a/docs/zcml/resource.rst +++ b/docs/zcml/asset.rst @@ -1,51 +1,51 @@ -.. _resource_directive: +.. _asset_directive: -``resource`` ------------- +``asset`` +--------- -The ``resource`` directive adds a resource override for a single -resource. +The ``asset`` directive adds an asset override for a single +static file/directory asset. Attributes ~~~~~~~~~~ ``to_override`` - A :term:`resource specification` specifying the resource to be + A :term:`asset specification` specifying the asset to be overridden. ``override_with`` - A :term:`resource specification` specifying the resource which + A :term:`asset specification` specifying the asset which is used as the override. Examples ~~~~~~~~ -.. topic:: Overriding a Single Resource File +.. topic:: Overriding a Single Asset File .. code-block:: xml :linenos: - <resource + <asset to_override="some.package:templates/mytemplate.pt" override_with="another.package:othertemplates/anothertemplate.pt" /> -.. topic:: Overriding all Resources in a Package +.. topic:: Overriding all Assets in a Package .. code-block:: xml :linenos: - <resource + <asset to_override="some.package" override_with="another.package" /> -.. topic:: Overriding all Resources in a Subdirectory of a Package +.. topic:: Overriding all Assets in a Subdirectory of a Package .. code-block:: xml :linenos: - <resource + <asset to_override="some.package:templates/" override_with="another.package:othertemplates/" /> @@ -53,10 +53,13 @@ Examples Alternatives ~~~~~~~~~~~~ -The :meth:`pyramid.config.Configurator.override_resource` +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:`resource_zcml_directive`. +See also :ref:`asset_zcml_directive`. diff --git a/docs/zcml/forbidden.rst b/docs/zcml/forbidden.rst index 7ea6b85fd..70f65069e 100644 --- a/docs/zcml/forbidden.rst +++ b/docs/zcml/forbidden.rst @@ -35,7 +35,7 @@ Attributes ``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 diff --git a/docs/zcml/handler.rst b/docs/zcml/handler.rst index 301bf7895..01d442ab6 100644 --- a/docs/zcml/handler.rst +++ b/docs/zcml/handler.rst @@ -28,7 +28,7 @@ Attributes ``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`` diff --git a/docs/zcml/notfound.rst b/docs/zcml/notfound.rst index a2ed95bc4..739eccd49 100644 --- a/docs/zcml/notfound.rst +++ b/docs/zcml/notfound.rst @@ -34,7 +34,7 @@ Attributes ``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 diff --git a/docs/zcml/route.rst b/docs/zcml/route.rst index 4f7cdb955..0f94fa11b 100644 --- a/docs/zcml/route.rst +++ b/docs/zcml/route.rst @@ -25,7 +25,7 @@ Attributes ``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`` @@ -172,7 +172,7 @@ Attributes ``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 diff --git a/docs/zcml/view.rst b/docs/zcml/view.rst index 74d497cb3..b4fabdc2c 100644 --- a/docs/zcml/view.rst +++ b/docs/zcml/view.rst @@ -51,7 +51,7 @@ Non-Predicate Attributes ``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 @@ -72,10 +72,10 @@ Non-Predicate Attributes 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 @@ -157,7 +157,7 @@ Predicate Attributes 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. @@ -222,7 +222,7 @@ Examples :linenos: <view - context=".models.MyModel" + context=".resources.MyResource" view=".views.hello_world" /> @@ -232,7 +232,7 @@ Examples :linenos: <view - context=".models.MyModel" + context=".resources.MyResource" view=".views.hello_world_post" request_method="POST" /> diff --git a/pyramid/asset.py b/pyramid/asset.py new file mode 100644 index 000000000..0f8483c6b --- /dev/null +++ b/pyramid/asset.py @@ -0,0 +1,207 @@ +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) diff --git a/pyramid/authorization.py b/pyramid/authorization.py index 4d2f3eb0e..f27369172 100644 --- a/pyramid/authorization.py +++ b/pyramid/authorization.py @@ -65,7 +65,7 @@ class ACLAuthorizationPolicy(object): 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: diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py index 6eb7af4d0..32896b8e9 100644 --- a/pyramid/chameleon_text.py +++ b/pyramid/chameleon_text.py @@ -71,7 +71,7 @@ def get_renderer(path): """ 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 @@ -90,7 +90,7 @@ def get_template(path): """ 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 @@ -109,7 +109,7 @@ deprecated( 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. @@ -130,7 +130,7 @@ deprecated( 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 diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py index 5c2d6f70f..f6093ea13 100644 --- a/pyramid/chameleon_zpt.py +++ b/pyramid/chameleon_zpt.py @@ -56,7 +56,7 @@ def get_renderer(path): """ 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 @@ -75,7 +75,7 @@ def get_template(path): """ 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 @@ -94,7 +94,7 @@ deprecated( 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. @@ -115,7 +115,7 @@ deprecated( 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 diff --git a/pyramid/config.py b/pyramid/config.py index 0932ae7b8..59c8706d0 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -73,8 +73,8 @@ from pyramid.path import package_of 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 @@ -327,14 +327,14 @@ class Configurator(object): 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, @@ -570,19 +570,21 @@ class Configurator(object): 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, renderers=DEFAULT_RENDERERS, debug_logger=None, @@ -848,7 +850,7 @@ class Configurator(object): 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) @@ -1061,7 +1063,7 @@ class Configurator(object): 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 @@ -1086,9 +1088,9 @@ class Configurator(object): 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 @@ -1472,11 +1474,10 @@ class Configurator(object): 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 @@ -1677,7 +1678,7 @@ class Configurator(object): 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 @@ -1858,20 +1859,20 @@ class Configurator(object): self.action((IRendererFactory, name), None) @action_method - def override_resource(self, to_override, override_with, _override=None): - """ Add a :app:`Pyramid` resource override to the current + def override_asset(self, to_override, override_with, _override=None): + """ Add a :app:`Pyramid` asset override to the current configuration state. - ``to_override`` is a :term:`resource specification` to the - resource being overridden. + ``to_override`` is a :term:`asset specification` to the + asset being overridden. - ``override_with`` is a :term:`resource specification` to the - resource that is performing the override. + ``override_with`` is a :term:`asset specification` to the + asset that is performing the override. - See :ref:`resources_chapter` for more - information about resource overrides.""" + See :ref:`assets_chapter` for more + information about asset overrides.""" if to_override == override_with: - raise ConfigurationError('You cannot override a resource with ' + raise ConfigurationError('You cannot override an asset with ' 'itself') package = to_override @@ -1905,6 +1906,8 @@ class Configurator(object): override(from_package, path, to_package, override_prefix) self.action(None, register) + override_resource = override_asset # bw compat + @action_method def set_forbidden_view(self, view=None, attr=None, renderer=None, wrapper=None): @@ -2094,7 +2097,7 @@ class Configurator(object): """ 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. @@ -2143,7 +2146,7 @@ class Configurator(object): @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 @@ -2152,40 +2155,39 @@ class Configurator(object): 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 @@ -2208,7 +2210,7 @@ class Configurator(object): 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 @@ -2237,7 +2239,7 @@ class Configurator(object): 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) @@ -2281,18 +2283,18 @@ class Configurator(object): 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: @@ -2301,14 +2303,16 @@ class Configurator(object): 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): @@ -2339,7 +2343,7 @@ class Configurator(object): 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 @@ -2507,7 +2511,7 @@ def _make_predicates(xhr=None, request_method=None, path_info=None, 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) diff --git a/pyramid/includes/meta.zcml b/pyramid/includes/meta.zcml index 58af814ef..9f3726cc9 100644 --- a/pyramid/includes/meta.zcml +++ b/pyramid/includes/meta.zcml @@ -41,9 +41,15 @@ /> <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 diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 94affd2bb..d9b06abae 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -163,7 +163,7 @@ class IAuthorizationPolicy(Interface): """ 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 """ diff --git a/pyramid/location.py b/pyramid/location.py index 7472129d2..1df2ae143 100644 --- a/pyramid/location.py +++ b/pyramid/location.py @@ -12,29 +12,29 @@ # ############################################################################## -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``. @@ -50,8 +50,8 @@ def lineage(model): 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 @@ -60,7 +60,7 @@ def lineage(model): # 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 diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py index a5f9b0f68..9d14ca8fe 100644 --- a/pyramid/mako_templating.py +++ b/pyramid/mako_templating.py @@ -4,10 +4,10 @@ import threading 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 @@ -18,12 +18,11 @@ class IMakoLookup(Interface): 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) @@ -33,7 +32,7 @@ class PkgResourceTemplateLookup(TemplateLookup): 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. """ @@ -45,8 +44,8 @@ class PkgResourceTemplateLookup(TemplateLookup): 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( @@ -75,9 +74,9 @@ def renderer_factory(info): '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) diff --git a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl index 2cc9c17e0..7627e75eb 100644 --- a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl @@ -1,12 +1,12 @@ 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() diff --git a/pyramid/paster_templates/starter/+package+/models.py b/pyramid/paster_templates/starter/+package+/models.py deleted file mode 100644 index 75dec7505..000000000 --- a/pyramid/paster_templates/starter/+package+/models.py +++ /dev/null @@ -1,7 +0,0 @@ -class MyModel(object): - pass - -root = MyModel() - -def get_root(request): - return root diff --git a/pyramid/paster_templates/starter/+package+/resources.py b/pyramid/paster_templates/starter/+package+/resources.py new file mode 100644 index 000000000..3d811895c --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/resources.py @@ -0,0 +1,3 @@ +class Root(object): + def __init__(self, request): + self.request = request diff --git a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl index 56d201f19..896aabca7 100644 --- a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl @@ -1,11 +1,11 @@ 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() diff --git a/pyramid/paster_templates/starter_zcml/+package+/configure.zcml b/pyramid/paster_templates/starter_zcml/+package+/configure.zcml index e5715718a..41e40866b 100644 --- a/pyramid/paster_templates/starter_zcml/+package+/configure.zcml +++ b/pyramid/paster_templates/starter_zcml/+package+/configure.zcml @@ -4,7 +4,7 @@ <include package="pyramid.includes" /> <view - context=".models.MyModel" + context=".resources.Root" view=".views.my_view" renderer="templates/mytemplate.pt" /> diff --git a/pyramid/paster_templates/starter_zcml/+package+/models.py b/pyramid/paster_templates/starter_zcml/+package+/models.py deleted file mode 100644 index 75dec7505..000000000 --- a/pyramid/paster_templates/starter_zcml/+package+/models.py +++ /dev/null @@ -1,7 +0,0 @@ -class MyModel(object): - pass - -root = MyModel() - -def get_root(request): - return root diff --git a/pyramid/paster_templates/starter_zcml/+package+/resources.py b/pyramid/paster_templates/starter_zcml/+package+/resources.py new file mode 100644 index 000000000..3d811895c --- /dev/null +++ b/pyramid/paster_templates/starter_zcml/+package+/resources.py @@ -0,0 +1,3 @@ +class Root(object): + def __init__(self, request): + self.request = request diff --git a/pyramid/renderers.py b/pyramid/renderers.py index 69c5f7275..c7fe86452 100644 --- a/pyramid/renderers.py +++ b/pyramid/renderers.py @@ -12,13 +12,13 @@ from pyramid.interfaces import IResponseFactory 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 @@ -31,14 +31,14 @@ def render(renderer_name, value, request=None, package=None): 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. @@ -75,13 +75,13 @@ def render_to_response(renderer_name, value, request=None, package=None): 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. @@ -114,11 +114,11 @@ def get_renderer(renderer_name, package=None): """ 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. """ @@ -164,11 +164,11 @@ class ChameleonRendererLookup(object): 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 @@ -204,7 +204,7 @@ class ChameleonRendererLookup(object): 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: @@ -218,10 +218,10 @@ class ChameleonRendererLookup(object): 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: diff --git a/pyramid/request.py b/pyramid/request.py index 6c9a1e421..74418f1bb 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -8,7 +8,7 @@ from pyramid.interfaces import ISessionFactory from pyramid.exceptions import ConfigurationError from pyramid.decorator import reify -from pyramid.url import model_url +from pyramid.url import resource_url from pyramid.url import route_url from pyramid.url import static_url from pyramid.url import route_path @@ -195,39 +195,45 @@ class Request(WebobRequest): """ return route_url(route_name, self, *elements, **kw) - def model_url(self, model, *elements, **kw): - """ Return the URL for the model object named ``model``, using - ``*elements`` and ``**kw`` as modifiers. + def resource_url(self, resource, *elements, **kw): + """ Return the URL for the :term:`resource` object named ``resource``, + using ``*elements`` and ``**kw`` as modifiers. This is a convenience method. The result of calling - :meth:`pyramid.request.Request.model_url` is the same as calling - :func:`pyramid.url.model_url` with an explicit ``request`` parameter. - - The :meth:`pyramid.request.Request.model_url` method calls the - :func:`pyramid.url.model_url` function using the Request object as - the ``request`` argument. The ``model``, ``*elements`` and ``*kw`` - arguments passed to :meth:`pyramid.request.Request.model_url` are - passed through to :func:`pyramid.url.model_url` unchanged and its + :meth:`pyramid.request.Request.resource_url` is the same as calling + :func:`pyramid.url.resource_url` with an explicit ``request`` parameter. + + The :meth:`pyramid.request.Request.resource_url` method calls the + :func:`pyramid.url.resource_url` function using the Request object as + the ``request`` argument. The ``resource``, ``*elements`` and ``*kw`` + arguments passed to :meth:`pyramid.request.Request.resource_url` are + passed through to :func:`pyramid.url.resource_url` unchanged and its result is returned. - This call to :meth:`pyramid.request.Request.model_url`:: + This call to :meth:`pyramid.request.Request.resource_url`:: - request.model_url(mymodel) + request.resource_url(myresource) - Is completely equivalent to calling :func:`pyramid.url.model_url` + Is completely equivalent to calling :func:`pyramid.url.resource_url` like this:: - from pyramid.url import model_url - model_url(model, request) + 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 diff --git a/pyramid/resource.py b/pyramid/resource.py index e1d25d0d4..5e8f3c968 100644 --- a/pyramid/resource.py +++ b/pyramid/resource.py @@ -1,207 +1,5 @@ -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 diff --git a/pyramid/router.py b/pyramid/router.py index 1b22a6a05..b665eeee2 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -22,7 +22,7 @@ from pyramid.exceptions import NotFound 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 @@ -127,7 +127,7 @@ class Router(object): # 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'], @@ -239,13 +239,13 @@ def make_app(root_factory, package=None, filename='configure.zcml', ``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, diff --git a/pyramid/security.py b/pyramid/security.py index 844cc05c8..723e87a87 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -26,7 +26,7 @@ DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) 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` @@ -81,7 +81,7 @@ def effective_principals(request): 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 diff --git a/pyramid/settings.py b/pyramid/settings.py index 1e49f7dd8..395694fa0 100644 --- a/pyramid/settings.py +++ b/pyramid/settings.py @@ -38,9 +38,14 @@ class Settings(dict): 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') @@ -52,7 +57,8 @@ class Settings(dict): '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, } diff --git a/pyramid/static.py b/pyramid/static.py index 645e78da1..5e4faf6fe 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -10,9 +10,9 @@ from paste.urlparser import StaticURLParser 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): @@ -108,7 +108,7 @@ class StaticURLInfo(object): 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):]``). @@ -162,7 +162,7 @@ class static_view(object): 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. @@ -179,13 +179,13 @@ class static_view(object): 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 @@ -193,7 +193,7 @@ class static_view(object): # (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: diff --git a/pyramid/testing.py b/pyramid/testing.py index 8d48bfdf4..4bcda906b 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -62,29 +62,34 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): 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`) listening for events of the type ``event_iface``. This method @@ -412,19 +417,19 @@ class DummyTemplateRenderer(object): 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: @@ -436,9 +441,9 @@ class DummyModel: 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 @@ -479,12 +484,12 @@ class DummyModel: 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) @@ -495,6 +500,8 @@ class DummyModel: inst.__parent__ = __parent__ return inst +DummyModel = DummyResource # b/w compat (forever) + class DummyRequest(object): """ A dummy request object (imitates a :term:`request` object). @@ -509,6 +516,9 @@ class DummyRequest(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' @@ -571,7 +581,7 @@ def setUp(registry=None, request=None, hook_zca=True, autocommit=True): 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. diff --git a/pyramid/tests/test_resource.py b/pyramid/tests/test_asset.py index 79d511dbd..ec45eb638 100644 --- a/pyramid/tests/test_resource.py +++ b/pyramid/tests/test_asset.py @@ -9,7 +9,7 @@ class TestOverrideProvider(unittest.TestCase): cleanUp() def _getTargetClass(self): - from pyramid.resource import OverrideProvider + from pyramid.asset import OverrideProvider return OverrideProvider def _makeOne(self, module): @@ -24,7 +24,7 @@ class TestOverrideProvider(unittest.TestCase): 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__)) @@ -34,7 +34,7 @@ class TestOverrideProvider(unittest.TestCase): 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__)) @@ -44,7 +44,7 @@ class TestOverrideProvider(unittest.TestCase): 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__)) @@ -53,14 +53,14 @@ class TestOverrideProvider(unittest.TestCase): 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) @@ -80,7 +80,7 @@ class TestOverrideProvider(unittest.TestCase): 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__)) @@ -92,7 +92,7 @@ class TestOverrideProvider(unittest.TestCase): 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__)) @@ -104,7 +104,7 @@ class TestOverrideProvider(unittest.TestCase): 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__)) @@ -115,7 +115,7 @@ class TestOverrideProvider(unittest.TestCase): 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) @@ -144,7 +144,7 @@ class TestOverrideProvider(unittest.TestCase): 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): @@ -152,7 +152,7 @@ class TestOverrideProvider(unittest.TestCase): 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): @@ -160,7 +160,7 @@ class TestOverrideProvider(unittest.TestCase): 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): @@ -168,7 +168,7 @@ class TestOverrideProvider(unittest.TestCase): 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): @@ -189,7 +189,7 @@ class TestOverrideProvider(unittest.TestCase): 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): @@ -259,39 +259,39 @@ class TestPackageOverrides(unittest.TestCase): 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 @@ -299,7 +299,7 @@ class TestPackageOverrides(unittest.TestCase): 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 @@ -323,7 +323,7 @@ class TestPackageOverrides(unittest.TestCase): 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): @@ -340,10 +340,10 @@ class TestDirectoryOverride(unittest.TestCase): 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 @@ -355,37 +355,37 @@ class Test_resolve_resource_spec(unittest.TestCase): 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): @@ -402,16 +402,16 @@ class TestFileOverride(unittest.TestCase): 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') diff --git a/pyramid/tests/test_authorization.py b/pyramid/tests/test_authorization.py index e44eb86c5..c4b2fb142 100644 --- a/pyramid/tests/test_authorization.py +++ b/pyramid/tests/test_authorization.py @@ -117,7 +117,7 @@ class TestACLAuthorizationPolicy(unittest.TestCase): 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 diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py index 025eb595b..b6a04426c 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -226,22 +226,22 @@ class ConfiguratorTests(unittest.TestCase): 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): @@ -2541,7 +2541,7 @@ class ConfiguratorTests(unittest.TestCase): 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) @@ -2972,27 +2972,27 @@ class ConfiguratorTests(unittest.TestCase): 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) @@ -3153,14 +3153,14 @@ class ConfiguratorTests(unittest.TestCase): 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) @@ -3179,7 +3179,7 @@ class ConfiguratorTests(unittest.TestCase): 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() diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py index 30ccef0a5..bec29fc10 100644 --- a/pyramid/tests/test_mako_templating.py +++ b/pyramid/tests/test_mako_templating.py @@ -77,7 +77,7 @@ class Test_renderer_factory(Base, unittest.TestCase): 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' @@ -94,7 +94,7 @@ class Test_renderer_factory(Base, unittest.TestCase): 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') @@ -364,28 +364,28 @@ class TestPkgResourceTemplateLookup(unittest.TestCase): 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]) diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py index bd6a0825d..9ff9e1274 100644 --- a/pyramid/tests/test_renderers.py +++ b/pyramid/tests/test_renderers.py @@ -143,11 +143,11 @@ class TestTemplateRendererFactory(unittest.TestCase): 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) @@ -166,10 +166,10 @@ class TestTemplateRendererFactory(unittest.TestCase): 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' diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index 7c6b9f2da..40940cfc9 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -241,11 +241,11 @@ class TestRequest(unittest.TestCase): 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): diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py index 26f6b0363..9a58280a1 100644 --- a/pyramid/tests/test_settings.py +++ b/pyramid/tests/test_settings.py @@ -45,40 +45,76 @@ class TestSettings(unittest.TestCase): 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({}) diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py index 40513be81..ec6fdac5f 100644 --- a/pyramid/tests/test_testing.py +++ b/pyramid/tests/test_testing.py @@ -33,13 +33,13 @@ class Test_registerDummySecurityPolicy(TestBase): 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'}) @@ -57,8 +57,8 @@ class Test_registerModels(TestBase): 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): @@ -331,10 +331,10 @@ class TestDummySecurityPolicy(unittest.TestCase): -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() @@ -344,26 +344,26 @@ class TestDummyModel(unittest.TestCase): 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') @@ -372,21 +372,21 @@ class TestDummyModel(unittest.TestCase): 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): diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py index 3245d6302..df9e1e554 100644 --- a/pyramid/tests/test_traversal.py +++ b/pyramid/tests/test_traversal.py @@ -62,7 +62,7 @@ class TraversalPathTests(unittest.TestCase): 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() @@ -70,8 +70,8 @@ class ModelGraphTraverserTests(unittest.TestCase): 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() @@ -272,68 +272,68 @@ class ModelGraphTraverserTests(unittest.TestCase): 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): @@ -396,10 +396,10 @@ class FindRootTests(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 @@ -409,124 +409,124 @@ class FindModelTests(unittest.TestCase): 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) @@ -536,7 +536,7 @@ class FindModelTests(unittest.TestCase): # # 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 @@ -544,15 +544,15 @@ class FindModelTests(unittest.TestCase): '/\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() @@ -624,10 +624,10 @@ class ModelPathTests(unittest.TestCase): 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() @@ -745,7 +745,7 @@ class TraversalContextURLTests(unittest.TestCase): 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 @@ -836,9 +836,9 @@ class TestVirtualRoot(unittest.TestCase): 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 @@ -885,96 +885,96 @@ class TraverseTests(unittest.TestCase): 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 @@ -984,11 +984,11 @@ class TraverseTests(unittest.TestCase): 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): diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index aad969ca7..f11d36aca 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -2,16 +2,16 @@ import unittest 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 diff --git a/pyramid/tests/test_zcml.py b/pyramid/tests/test_zcml.py index 8bc7c3eac..4c32e4d52 100644 --- a/pyramid/tests/test_zcml.py +++ b/pyramid/tests/test_zcml.py @@ -815,7 +815,7 @@ class TestStaticDirective(unittest.TestCase): 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() @@ -824,8 +824,8 @@ class TestResourceDirective(unittest.TestCase): 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 @@ -1302,7 +1302,7 @@ class Test_path_spec(unittest.TestCase): 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') diff --git a/pyramid/traversal.py b/pyramid/traversal.py index fb73ad906..086823695 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -15,31 +15,31 @@ from pyramid.exceptions import URLDecodeError from pyramid.location import lineage from pyramid.threadlocal import get_current_registry -def find_root(model): - """ Find the root node in the graph to which ``model`` - belongs. Note that ``model`` should be :term:`location`-aware. - Note that the root node is available in the request object by +def find_root(resource): + """ Find the root node in the graph to which ``resource`` + belongs. Note that ``resource`` should be :term:`location`-aware. + Note that the root resource is available in the request object by accessing the ``request.root`` attribute. """ - for location in lineage(model): + for location in lineage(resource): if location.__parent__ is None: - model = location + resource = location break - return model + return resource -def find_model(model, path): - """ Given a model object and a string or tuple representing a path +def find_resource(resource, path): + """ Given a resource object and a string or tuple representing a path (such as the return value of - :func:`pyramid.traversal.model_path` or - :func:`pyramid.traversal.model_path_tuple`), return a context - in this application's model graph at the specified path. The - model passed in *must* be :term:`location`-aware. If the path + :func:`pyramid.traversal.resource_path` or + :func:`pyramid.traversal.resource_path_tuple`), return a context + in this application's resource tree at the specified path. The + resource passed in *must* be :term:`location`-aware. If the path cannot be resolved (if the respective node in the graph does not exist), a :exc:`KeyError` will be raised. This function is the logical inverse of - :func:`pyramid.traversal.model_path` and - :func:`pyramid.traversal.model_path_tuple`; it can resolve any + :func:`pyramid.traversal.resource_path` and + :func:`pyramid.traversal.resource_path_tuple`; it can resolve any path string or tuple generated by either of those functions. Rules for passing a *string* as the ``path`` argument: if the @@ -47,15 +47,15 @@ def find_model(model, path): character, the path will considered absolute and the graph traversal will start at the root object. If the first character of the path string is *not* the ``/`` character, the path is - considered relative and graph traversal will begin at the model - object supplied to the function as the ``model`` argument. If an - empty string is passed as ``path``, the ``model`` passed in will - be returned. Model path strings must be escaped in the following + considered relative and graph traversal will begin at the resource + object supplied to the function as the ``resource`` argument. If an + empty string is passed as ``path``, the ``resource`` passed in will + be returned. Resource path strings must be escaped in the following manner: each Unicode path segment must be encoded as UTF-8 and as each path segment must escaped via Python's :mod:`urllib.quote`. For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or ``to%20the/La%20Pe%C3%B1a`` (relative). The - :func:`pyramid.traversal.model_path` function generates strings + :func:`pyramid.traversal.resource_path` function generates strings which follow these rules (albeit only absolute ones). Rules for passing a *tuple* as the ``path`` argument: if the first @@ -64,108 +64,117 @@ def find_model(model, path): traversal will start at the graph root object. If the first element in the path tuple is not the empty string (for example ``('a', 'b', 'c')``), the path is considered relative and graph - traversal will begin at the model object supplied to the function - as the ``model`` argument. If an empty sequence is passed as - ``path``, the ``model`` passed in itself will be returned. No + traversal will begin at the resource object supplied to the function + as the ``resource`` argument. If an empty sequence is passed as + ``path``, the ``resource`` passed in itself will be returned. No URL-quoting or UTF-8-encoding of individual path segments within the tuple is required (each segment may be any string or unicode - object representing a model name). Model path tuples generated by - :func:`pyramid.traversal.model_path_tuple` can always be - resolved by ``find_model``. + object representing a resource name). Resource path tuples generated by + :func:`pyramid.traversal.resource_path_tuple` can always be + resolved by ``find_resource``. + + .. 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). - - Model path strings returned will be escaped in the following +def resource_path(resource, *elements): + """ Return a string object representing the absolute physical path of the + resource object based on its position in the resource tree, e.g + ``/foo/bar``. Any positional arguments passed in as ``elements`` will be + appended as path segments to the end of the resource path. For instance, + if the resource's path is ``/foo/bar`` and ``elements`` equals ``('a', + 'b')``, the returned string will be ``/foo/bar/a/b``. The first + character in the string will always be the ``/`` character (a leading + ``/`` character in a path string represents that the path is absolute). + + Resource path strings returned will be escaped in the following manner: each unicode path segment will be encoded as UTF-8 and each path segment will be escaped via Python's :mod:`urllib.quote`. For example, ``/path/to%20the/La%20Pe%C3%B1a``. This function is a logical inverse of - :mod:`pyramid.traversal.find_model`: it can be used to generate + :mod:`pyramid.traversal.find_resource`: it can be used to generate path references that can later be resolved via that function. - The ``model`` passed in *must* be :term:`location`-aware. + The ``resource`` passed in *must* be :term:`location`-aware. .. note:: Each segment in the path string returned will use the - ``__name__`` attribute of the model it represents within + ``__name__`` attribute of the resource it represents within the graph. Each of these segments *should* be a unicode or string object (as per the contract of :term:`location`-awareness). However, no conversion or - safety checking of model names is performed. For - instance, if one of the models in your graph has a + safety checking of resource names is performed. For + instance, if one of the resources in your tree has a ``__name__`` which (by error) is a dictionary, the - :func:`pyramid.traversal.model_path` function will + :func:`pyramid.traversal.resource_path` function will attempt to append it to a string and it will cause a :exc:`pyramid.exceptions.URLDecodeError`. - .. note:: The :term:`root` model *must* have a ``__name__`` + .. note:: The :term:`root` resource *must* have a ``__name__`` attribute with a value of either ``None`` or the empty string for paths to be generated properly. If the root - model has a non-null ``__name__`` attribute, its name + resource has a non-null ``__name__`` attribute, its name will be prepended to the generated path rather than a single leading '/' character. + + .. 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)) + +model_path = resource_path # b/w compat -def traverse(model, path): - """Given a model object as ``model`` and a string or tuple +def traverse(resource, path): + """Given a resource object as ``resource`` and a string or tuple representing a path as ``path`` (such as the return value of - :func:`pyramid.traversal.model_path` or - :func:`pyramid.traversal.model_path_tuple` or the value of + :func:`pyramid.traversal.resource_path` or + :func:`pyramid.traversal.resource_path_tuple` or the value of ``request.environ['PATH_INFO']``), return a dictionary with the keys ``context``, ``root``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. A definition of each value in the returned dictionary: - - ``context``: The :term:`context` (a :term:`model` object) found + - ``context``: The :term:`context` (a :term:`resource` object) found via traversal or url dispatch. If the ``path`` passed in is the - empty string, the value of the ``model`` argument passed to this + empty string, the value of the ``resource`` argument passed to this function is returned. - - ``root``: The model object at which :term:`traversal` begins. - If the ``model`` passed in was found via url dispatch or if the + - ``root``: The resource object at which :term:`traversal` begins. + If the ``resource`` passed in was found via url dispatch or if the ``path`` passed in was relative (non-absolute), the value of the - ``model`` argument passed to this function is returned. + ``resource`` argument passed to this function is returned. - ``view_name``: The :term:`view name` found during - :term:`traversal` or :term:`url dispatch`; if the ``model`` was + :term:`traversal` or :term:`url dispatch`; if the ``resource`` was found via traversal, this is usually a representation of the path segment which directly follows the path to the ``context`` in the ``path``. The ``view_name`` will be a Unicode object or @@ -173,11 +182,11 @@ def traverse(model, path): there is no element which follows the ``context`` path. An example: if the path passed is ``/foo/bar``, and a context object is found at ``/foo`` (but not at ``/foo/bar``), the 'view - name' will be ``u'bar'``. If the ``model`` was found via + name' will be ``u'bar'``. If the ``resource`` was found via urldispatch, the view_name will be the name the route found was registered with. - - ``subpath``: For a ``model`` found via :term:`traversal`, this + - ``subpath``: For a ``resource`` found via :term:`traversal`, this is a sequence of path segments found in the ``path`` that follow the ``view_name`` (if any). Each of these items is a Unicode object. If no path segments follow the ``view_name``, the @@ -185,7 +194,7 @@ def traverse(model, path): passed is ``/foo/bar/baz/buz``, and a context object is found at ``/foo`` (but not ``/foo/bar``), the 'view name' will be ``u'bar'`` and the :term:`subpath` will be ``[u'baz', u'buz']``. - For a ``model`` found via url dispatch, the subpath will be a + For a ``resource`` found via url dispatch, the subpath will be a sequence of values discerned from ``*subpath`` in the route pattern matched or the empty sequence. @@ -194,27 +203,27 @@ def traverse(model, path): Each of these items is a Unicode object. If no path segments were traversed to find the ``context`` object (e.g. if the ``path`` provided is the empty string), the ``traversed`` value - will be the empty sequence. If the ``model`` is a model found + will be the empty sequence. If the ``resource`` is a resource found via :term:`url dispatch`, traversed will be None. - - ``virtual_root``: A model object representing the 'virtual' root + - ``virtual_root``: A resource object representing the 'virtual' root of the object graph being traversed during :term:`traversal`. See :ref:`vhosting_chapter` for a definition of the virtual root object. If no virtual hosting is in effect, and the ``path`` passed in was absolute, the ``virtual_root`` will be the *physical* root object (the object at which :term:`traversal` - begins). If the ``model`` passed in was found via :term:`URL + begins). If the ``resource`` passed in was found via :term:`URL dispatch` or if the ``path`` passed in was relative, the ``virtual_root`` will always equal the ``root`` object (the - model passed in). + resource passed in). - ``virtual_root_path`` -- If :term:`traversal` was used to find - the ``model``, this will be the sequence of path elements + the ``resource``, this will be the sequence of path elements traversed to find the ``virtual_root`` object. Each of these items is a Unicode object. If no path segments were traversed to find the ``virtual_root`` object (e.g. if virtual hosting is not in effect), the ``traversed`` value will be the empty list. - If url dispatch was used to find the ``model``, this will be + If url dispatch was used to find the ``resource``, this will be ``None``. If the path cannot be resolved, a :exc:`KeyError` will be raised. @@ -224,15 +233,15 @@ def traverse(model, path): character, the path will considered absolute and the graph traversal will start at the root object. If the first character of the path string is *not* the ``/`` character, the path is - considered relative and graph traversal will begin at the model - object supplied to the function as the ``model`` argument. If an - empty string is passed as ``path``, the ``model`` passed in will - be returned. Model path strings must be escaped in the following + considered relative and graph traversal will begin at the resource + object supplied to the function as the ``resource`` argument. If an + empty string is passed as ``path``, the ``resource`` passed in will + be returned. Resource path strings must be escaped in the following manner: each Unicode path segment must be encoded as UTF-8 and each path segment must escaped via Python's :mod:`urllib.quote`. For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or ``to%20the/La%20Pe%C3%B1a`` (relative). The - :func:`pyramid.traversal.model_path` function generates strings + :func:`pyramid.traversal.resource_path` function generates strings which follow these rules (albeit only absolute ones). Rules for passing a *tuple* as the ``path`` argument: if the first @@ -241,12 +250,12 @@ def traverse(model, path): traversal will start at the graph root object. If the first element in the path tuple is not the empty string (for example ``('a', 'b', 'c')``), the path is considered relative and graph - traversal will begin at the model object supplied to the function - as the ``model`` argument. If an empty sequence is passed as - ``path``, the ``model`` passed in itself will be returned. No + traversal will begin at the resource object supplied to the function + as the ``resource`` argument. If an empty sequence is passed as + ``path``, the ``resource`` passed in itself will be returned. No URL-quoting or UTF-8-encoding of individual path segments within the tuple is required (each segment may be any string or unicode - object representing a model name). + object representing a resource name). Explanation of the conversion of ``path`` segment values to Unicode during traversal: Each segment is URL-unquoted, and @@ -284,7 +293,7 @@ def traverse(model, path): path = path.encode('ascii') if path and path[0] == '/': - model = find_root(model) + resource = find_root(resource) reg = get_current_registry() @@ -295,19 +304,19 @@ def traverse(model, path): request = request_factory.blank(path) request.registry = reg - traverser = reg.queryAdapter(model, ITraverser) + traverser = reg.queryAdapter(resource, ITraverser) if traverser is None: - traverser = ModelGraphTraverser(model) + traverser = ResourceTreeTraverser(resource) return traverser(request) -def model_path_tuple(model, *elements): +def resource_path_tuple(resource, *elements): """ Return a tuple representing the absolute physical path of the - ``model`` object based on its position in an object graph, e.g + ``resource`` object based on its position in an object graph, e.g ``('', 'foo', 'bar')``. Any positional arguments passed in as ``elements`` will be appended as elements in the tuple - representing the model path. For instance, if the model's + representing the resource path. For instance, if the resource's path is ``('', 'foo', 'bar')`` and elements equals ``('a', 'b')``, the returned tuple will be ``('', 'foo', 'bar', 'a', b')``. The first element of this tuple will always be the empty string (a @@ -315,52 +324,58 @@ def model_path_tuple(model, *elements): path is absolute). This function is a logical inverse of - :func:`pyramid.traversal.find_model`: it can be used to + :func:`pyramid.traversal.find_resource`: it can be used to generate path references that can later be resolved that function. - The ``model`` passed in *must* be :term:`location`-aware. + The ``resource`` passed in *must* be :term:`location`-aware. .. note:: Each segment in the path tuple returned will equal the - ``__name__`` attribute of the model it represents within + ``__name__`` attribute of the resource it represents within the graph. Each of these segments *should* be a unicode or string object (as per the contract of :term:`location`-awareness). However, no conversion or - safety checking of model names is performed. For - instance, if one of the models in your graph has a + safety checking of resource names is performed. For + instance, if one of the resources in your tree has a ``__name__`` which (by error) is a dictionary, that dictionary will be placed in the path tuple; no warning or error will be given. - .. note:: The :term:`root` model *must* have a ``__name__`` + .. note:: The :term:`root` resource *must* have a ``__name__`` attribute with a value of either ``None`` or the empty string for path tuples to be generated properly. If - the root model has a non-null ``__name__`` attribute, + the root resource has a non-null ``__name__`` attribute, its name will be the first element in the generated path tuple rather than the empty string. + + .. note:: For backwards compatibility purposes, this function can also + be imported as ``model_path_tuple``. + """ - return tuple(_model_path_list(model, *elements)) + return tuple(_resource_path_list(resource, *elements)) -def _model_path_list(model, *elements): - """ Implementation detail shared by model_path and model_path_tuple """ - path = [loc.__name__ or '' for loc in lineage(model)] +def _resource_path_list(resource, *elements): + """ Implementation detail shared by resource_path and resource_path_tuple""" + path = [loc.__name__ or '' for loc in lineage(resource)] path.reverse() path.extend(elements) return path -def virtual_root(model, request): +_model_path_list = _resource_path_list # b/w compat + +def virtual_root(resource, request): """ - Provided any :term:`model` and a :term:`request` object, return - the model object representing the :term:`virtual root` of the + Provided any :term:`resource` and a :term:`request` object, return + the resource object representing the :term:`virtual root` of the current :term:`request`. Using a virtual root in a :term:`traversal` -based :app:`Pyramid` application permits rooting, for example, the object at the traversal path ``/cms`` at ``http://example.com/`` instead of rooting it at ``http://example.com/cms/``. - If the ``model`` passed in is a context obtained via + If the ``resource`` passed in is a context obtained via :term:`traversal`, and if the ``HTTP_X_VHM_ROOT`` key is in the WSGI environment, the value of this key will be treated as a - 'virtual root path': the :func:`pyramid.traversal.find_model` + 'virtual root path': the :func:`pyramid.traversal.find_resource` API will be used to find the virtual root object using this path; if the object is found, it will be returned. If the ``HTTP_X_VHM_ROOT`` key is is not present in the WSGI environment, @@ -370,16 +385,16 @@ def virtual_root(model, request): :term:`URL dispatch`. Contexts obtained via URL dispatch don't really support being virtually rooted (each URL dispatch context is both its own physical and virtual root). However if this API - is called with a ``model`` argument which is a context obtained - via URL dispatch, the model passed in will be returned + is called with a ``resource`` argument which is a context obtained + via URL dispatch, the resource passed in will be returned unconditionally.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c - urlgenerator = reg.queryMultiAdapter((model, request), IContextURL) + urlgenerator = reg.queryMultiAdapter((resource, request), IContextURL) if urlgenerator is None: - urlgenerator = TraversalContextURL(model, request) + urlgenerator = TraversalContextURL(resource, request) return urlgenerator.virtual_root() @lru_cache(1000) @@ -438,13 +453,13 @@ def traversal_path(path): (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 @@ -474,7 +489,7 @@ _segment_cache = {} def quote_path_segment(segment): """ Return a quoted representation of a 'path segment' (such as - the string ``__name__`` attribute of a model) as a string. If the + the string ``__name__`` attribute of a resource) as a string. If the ``segment`` passed in is a unicode object, it is converted to a UTF-8 string, then it is URL-quoted using Python's ``urllib.quote``. If the ``segment`` passed in is a string, it is @@ -510,10 +525,10 @@ def quote_path_segment(segment): _segment_cache[segment] = result return result -class ModelGraphTraverser(object): - """ A model graph traverser that should be used (for speed) when - every object in the graph supplies a ``__name__`` and - ``__parent__`` attribute (ie. every object in the graph is +class ResourceTreeTraverser(object): + """ A resource tree traverser that should be used (for speed) when + every object in the tree supplies a ``__name__`` and + ``__parent__`` attribute (ie. every object in the tree is :term:`location` aware) .""" implements(ITraverser) @@ -616,9 +631,11 @@ class ModelGraphTraverser(object): 'traversed':vpath_tuple, 'virtual_root':vroot, 'virtual_root_path':vroot_tuple, 'root':root} +ModelGraphTraverser = ResourceTreeTraverser # b/w compat + class TraversalContextURL(object): """ The IContextURL adapter used to generate URLs for a context - object obtained via graph traversal""" + object obtained via resource tree traversal""" implements(IContextURL) vroot_varname = VH_ROOT_KEY @@ -631,7 +648,7 @@ class TraversalContextURL(object): environ = self.request.environ vroot_varname = self.vroot_varname if vroot_varname in environ: - return find_model(self.context, environ[vroot_varname]) + return find_resource(self.context, environ[vroot_varname]) # shortcut instead of using find_root; we probably already # have it on the request try: @@ -641,15 +658,15 @@ class TraversalContextURL(object): def __call__(self): """ Generate a URL based on the :term:`lineage` of a - :term:`model` object obtained via :term:`traversal`. If any - model in the context lineage has a Unicode name, it will be + :term:`resource` object obtained via :term:`traversal`. If any + resource in the context lineage has a Unicode name, it will be converted to a UTF-8 string before being attached to the URL. If a ``HTTP_X_VHM_ROOT`` key is present in the WSGI environment, its value will be treated as a 'virtual root path': the path of the URL generated by this will be left-stripped of this virtual root path value. """ - path = model_path(self.context) + path = resource_path(self.context) if path != '/': path = path + '/' request = self.request diff --git a/pyramid/url.py b/pyramid/url.py index 79740821e..6ae0ce8c4 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -73,9 +73,9 @@ def route_url(route_name, request, *elements, **kw): If a keyword argument ``_anchor`` is present, its string representation will be used as a named anchor in the generated URL - (e.g. if ``_anchor`` is passed as ``foo`` and the model URL is - ``http://example.com/model/url``, the resulting generated URL will - be ``http://example.com/model/url#foo``). + (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is + ``http://example.com/route/url``, the resulting generated URL will + be ``http://example.com/route/url#foo``). .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 encoded. If ``_anchor`` is passed as a Unicode object, it @@ -181,42 +181,42 @@ def route_path(route_name, request, *elements, **kw): kw['_app_url'] = '' return route_url(route_name, request, *elements, **kw) -def model_url(model, request, *elements, **kw): +def resource_url(resource, request, *elements, **kw): """ - Generate a string representing the absolute URL of the ``model`` + Generate a string representing the absolute URL of the :term:`resource` object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or ``SERVER_NAME`` in the ``request``, plus any ``SCRIPT_NAME``. The overall result of this function is always a UTF-8 encoded string (never Unicode). - .. note:: Calling :meth:`pyramid.Request.model_url` can be used to - achieve the same result as :func:`pyramid.url.model_url`. + .. note:: Calling :meth:`pyramid.Request.resource_url` can be used to + achieve the same result as :func:`pyramid.url.resource_url`. Examples:: - model_url(context, request) => + resource_url(context, request) => http://example.com/ - model_url(context, request, 'a.html') => + resource_url(context, request, 'a.html') => http://example.com/a.html - model_url(context, request, 'a.html', query={'q':'1'}) => + resource_url(context, request, 'a.html', query={'q':'1'}) => http://example.com/a.html?q=1 - model_url(context, request, 'a.html', anchor='abc') => + resource_url(context, request, 'a.html', anchor='abc') => http://example.com/a.html#abc Any positional arguments passed in as ``elements`` must be strings or Unicode objects. These will be joined by slashes and appended - to the generated model URL. Each of the elements passed in is + to the generated resource URL. Each of the elements passed in is URL-quoted before being appended; if any element is Unicode, it will converted to a UTF-8 bytestring before being URL-quoted. - .. warning:: if no ``elements`` arguments are specified, the model + .. warning:: if no ``elements`` arguments are specified, the resource URL will end with a trailing slash. If any ``elements`` are used, the generated URL will *not* end in trailing a slash. @@ -241,9 +241,9 @@ def model_url(model, request, *elements, **kw): If a keyword argument ``anchor`` is present, its string representation will be used as a named anchor in the generated URL - (e.g. if ``anchor`` is passed as ``foo`` and the model URL is - ``http://example.com/model/url``, the resulting generated URL will - be ``http://example.com/model/url#foo``). + (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is + ``http://example.com/resource/url``, the resulting generated URL will + be ``http://example.com/resource/url#foo``). .. note:: If ``anchor`` is passed as a string, it should be UTF-8 encoded. If ``anchor`` is passed as a Unicode object, it @@ -255,28 +255,31 @@ def model_url(model, request, *elements, **kw): will always follow the query element, e.g. ``http://example.com?foo=1#bar``. - .. note:: If the ``model`` used is the result of a + .. note:: If the :term:`resource` used is the result of a :term:`traversal`, it must be :term:`location`-aware. - The ``model`` can also be the context of a :term:`URL + The resource can also be the context of a :term:`URL dispatch`; contexts found this way do not need to be location-aware. .. note:: If a 'virtual root path' is present in the request environment (the value of the WSGI environ key - ``HTTP_X_VHM_ROOT``), and the ``model`` was obtained via + ``HTTP_X_VHM_ROOT``), and the resource was obtained via :term:`traversal`, the URL path will not include the virtual root prefix (it will be stripped off the left hand side of the generated URL). + + .. 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 = '' @@ -295,15 +298,17 @@ def model_url(model, request, *elements, **kw): else: suffix = '' - return model_url + suffix + qs + anchor + return resource_url + suffix + qs + anchor + +model_url = resource_url # 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`. @@ -317,7 +322,7 @@ def static_url(path, request, **kw): 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). @@ -328,7 +333,7 @@ def static_url(path, request, **kw): 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. @@ -336,7 +341,7 @@ def static_url(path, request, **kw): """ 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 diff --git a/pyramid/view.py b/pyramid/view.py index d18d0dba3..cd313e5a8 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -147,9 +147,9 @@ class view_config(object): 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' @@ -158,14 +158,14 @@ class view_config(object): :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' diff --git a/pyramid/zcml.py b/pyramid/zcml.py index db314efa9..f668e3b4b 100644 --- a/pyramid/zcml.py +++ b/pyramid/zcml.py @@ -21,7 +21,7 @@ from pyramid.authentication import RepozeWho1AuthenticationPolicy 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 ########################## @@ -402,23 +402,23 @@ def forbidden(_context, wrapper=wrapper) -class IResourceDirective(Interface): +class IAssetDirective(Interface): """ - Directive for specifying that one package may override resources from + Directive for specifying that one package may override assets from another package. """ to_override = TextLine( title=u"Override spec", - description=u'The spec of the resource to override.', + description=u'The spec of the asset to override.', required=True) override_with = TextLine( title=u"With spec", - description=u"The spec of the resource providing the override.", + description=u"The spec of the asset providing the override.", required=True) -def resource(_context, to_override, override_with, _override=None): +def asset(_context, to_override, override_with, _override=None): config = Configurator.with_context(_context) - config.override_resource(to_override, override_with, _override=_override) + config.override_asset(to_override, override_with, _override=_override) class IRepozeWho1AuthenticationPolicyDirective(Interface): identifier_name = TextLine(title=u'identitfier_name', required=False, @@ -524,7 +524,7 @@ class IStaticDirective(Interface): 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) @@ -807,14 +807,14 @@ def default_permission(_context, name): 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): |
