pyramid-1.4.5/ 0000775 0001750 0001750 00000000000 12212235513 012456 5 ustar takaki takaki pyramid-1.4.5/CHANGES.txt 0000664 0001750 0001750 00000101606 12210156141 014270 0 ustar takaki takaki 1.4.5 (201-08-30)
=================
Bug Fixes
---------
- The ``alchemy`` scaffold would break when the database was MySQL during
tables creation. See https://github.com/Pylons/pyramid/pull/1049. Backport
from master.
- It was not possible to use ``pyramid.httpexceptions.HTTPException`` as
the ``context`` of an exception view as very general catchall for
http-related exceptions when you wanted that exception view to override the
default exception view. See https://github.com/Pylons/pyramid/issues/985.
Backport from master.
- When the ``pyramid.reload_templates`` setting was true, and a Chameleon
template was reloaded, and the renderer specification named a macro
(e.g. ``foo#macroname.pt``), renderings of the template after the template
was reloaded due to a file change would produce the entire template body
instead of just a rendering of the macro. See
https://github.com/Pylons/pyramid/issues/1013. Backport from master.
- Fixed a Mako renderer bug returning a tuple with a previous defname value
in some circumstances. See https://github.com/Pylons/pyramid/issues/1037 for
more information. Backport from master.
- Make ``pserve.cherrypy_server_runner`` Python 3 compatible. See
https://github.com/Pylons/pyramid/issues/718. Backport from master.
1.4.4 (2013-08-27)
==================
Bug Fixes
---------
- Fix an obscure problem when combining a virtual root with a route with a
``*traverse`` in its pattern. Now the traversal path generated in
such a configuration will be correct, instead of an element missing
a leading slash.
1.4.3 (2013-07-18)
==================
Bug Fixes
---------
- ``pyramid.testing.DummyResource`` didn't define ``__bool__``, so code under
Python 3 would use ``__len__`` to find truthiness; this usually caused an
instance of DummyResource to be "falsy" instead of "truthy". See
https://github.com/Pylons/pyramid/pull/1032
1.4.2 (2013-05-21)
==================
Bug Fixes
---------
- ``mako_templating``: added defensive workaround for non-importability of
``mako`` due to upstream ``markupsafe`` dropping Python 3.2 support. Mako
templating will no longer work under the combination of MarkupSafe 0.17 and
Python 3.2 (although the combination of MarkupSafe 0.17 and Python 3.3 or any
supported Python 2 version will work OK).
- Make the ``pyramid.config.assets.PackageOverrides`` object implement the API
for ``__loader__`` objects specified in PEP 302. Proxies to the
``__loader__`` set by the importer, if present; otherwise, raises
``NotImplementedError``. This makes Pyramid static view overrides work
properly under Python 3.3 (previously they would not). See
https://github.com/Pylons/pyramid/pull/1015 for more information.
1.4.1 (2013-04-23)
==================
Bug Fixes
---------
- Spaces and dots may now be in mako renderer template paths. This was
broken when support for the new makodef syntax was added in 1.4a1.
See https://github.com/Pylons/pyramid/issues/950
- ``pyramid.debug_authorization=true`` will now correctly print out
``Allowed`` for views registered with ``NO_PERMISSION_REQUIRED`` instead
of invoking the ``permits`` method of the authorization policy.
See https://github.com/Pylons/pyramid/issues/954
- Pyramid failed to install on some systems due to being packaged with
some test files containing higher order characters in their names. These
files have now been removed. See
https://github.com/Pylons/pyramid/issues/981
1.4 (2012-12-18)
================
Docs
----
- Fix functional tests in the ZODB tutorial
1.4b3 (2012-12-10)
==================
- Packaging release only, no code changes. 1.4b2 was a brownbag release due to
missing directories in the tarball.
1.4b2 (2012-12-10)
==================
Docs
----
- Scaffolding is now PEP-8 compliant (at least for a brief shining moment).
- Tutorial improvements.
Backwards Incompatibilities
---------------------------
- Modified the ``_depth`` argument to ``pyramid.view.view_config`` to accept
a value relative to the invocation of ``view_config`` itself. Thus, when it
was previously expecting a value of ``1`` or greater, to reflect that
the caller of ``view_config`` is 1 stack frame away from ``venusian.attach``,
this implementation detail is now hidden.
- Modified the ``_backframes`` argument to ``pyramid.util.action_method`` in a
similar way to the changes described to ``_depth`` above. This argument
remains undocumented, but might be used in the wild by some insane person.
1.4b1 (2012-11-21)
==================
Features
--------
- Small microspeed enhancement which anticipates that a
``pyramid.response.Response`` object is likely to be returned from a view.
Some code is shortcut if the class of the object returned by a view is this
class. A similar microoptimization was done to
``pyramid.request.Request.is_response``.
- Make it possible to use variable arguments on ``p*`` commands (``pserve``,
``pshell``, ``pviews``, etc) in the form ``a=1 b=2`` so you can fill in
values in parameterized ``.ini`` file, e.g. ``pshell etc/development.ini
http_port=8080``. See https://github.com/Pylons/pyramid/pull/714
- A somewhat advanced and obscure feature of Pyramid event handlers is their
ability to handle "multi-interface" notifications. These notifications have
traditionally presented multiple objects to the subscriber callable. For
instance, if an event was sent by code like this::
registry.notify(event, context)
In the past, in order to catch such an event, you were obligated to write and
register an event subscriber that mentioned both the event and the context in
its argument list::
@subscriber([SomeEvent, SomeContextType])
def asubscriber(event, context):
pass
In many subscriber callables registered this way, it was common for the logic
in the subscriber callable to completely ignore the second and following
arguments (e.g. ``context`` in the above example might be ignored), because
they usually existed as attributes of the event anyway. You could usually
get the same value by doing ``event.context`` or similar.
The fact that you needed to put an extra argument which you usually ignored
in the subscriber callable body was only a minor annoyance until we added
"subscriber predicates", used to narrow the set of circumstances under which
a subscriber will be executed, in a prior 1.4 alpha release. Once those were
added, the annoyance was escalated, because subscriber predicates needed to
accept the same argument list and arity as the subscriber callables that they
were configured against. So, for example, if you had these two subscriber
registrations in your code::
@subscriber([SomeEvent, SomeContextType])
def asubscriber(event, context):
pass
@subscriber(SomeOtherEvent)
def asubscriber(event):
pass
And you wanted to use a subscriber predicate::
@subscriber([SomeEvent, SomeContextType], mypredicate=True)
def asubscriber1(event, context):
pass
@subscriber(SomeOtherEvent, mypredicate=True)
def asubscriber2(event):
pass
If an existing ``mypredicate`` subscriber predicate had been written in such
a way that it accepted only one argument in its ``__call__``, you could not
use it against a subscription which named more than one interface in its
subscriber interface list. Similarly, if you had written a subscriber
predicate that accepted two arguments, you couldn't use it against a
registration that named only a single interface type.
For example, if you created this predicate::
class MyPredicate(object):
# portions elided...
def __call__(self, event):
return self.val == event.context.foo
It would not work against a multi-interface-registered subscription, so in
the above example, when you attempted to use it against ``asubscriber1``, it
would fail at runtime with a TypeError, claiming something was attempting to
call it with too many arguments.
To hack around this limitation, you were obligated to design the
``mypredicate`` predicate to expect to receive in its ``__call__`` either a
single ``event`` argument (a SomeOtherEvent object) *or* a pair of arguments
(a SomeEvent object and a SomeContextType object), presumably by doing
something like this::
class MyPredicate(object):
# portions elided...
def __call__(self, event, context=None):
return self.val == event.context.foo
This was confusing and bad.
In order to allow people to ignore unused arguments to subscriber callables
and to normalize the relationship between event subscribers and subscriber
predicates, we now allow both subscribers and subscriber predicates to accept
only a single ``event`` argument even if they've been subscribed for
notifications that involve multiple interfaces. Subscribers and subscriber
predicates that accept only one argument will receive the first object passed
to ``notify``; this is typically (but not always) the event object. The
other objects involved in the subscription lookup will be discarded. You can
now write an event subscriber that accepts only ``event`` even if it
subscribes to multiple interfaces::
@subscriber([SomeEvent, SomeContextType])
def asubscriber(event):
# this will work!
This prevents you from needing to match the subscriber callable parameters to
the subscription type unnecessarily, especially when you don't make use of
any argument in your subscribers except for the event object itself.
Note, however, that if the event object is not the first
object in the call to ``notify``, you'll run into trouble. For example, if
notify is called with the context argument first::
registry.notify(context, event)
You won't be able to take advantage of the event-only feature. It will
"work", but the object received by your event handler won't be the event
object, it will be the context object, which won't be very useful::
@subscriber([SomeContextType, SomeEvent])
def asubscriber(event):
# bzzt! you'll be getting the context here as ``event``, and it'll
# be useless
Existing multiple-argument subscribers continue to work without issue, so you
should continue use those if your system notifies using multiple interfaces
and the first interface is not the event interface. For example::
@subscriber([SomeContextType, SomeEvent])
def asubscriber(context, event):
# this will still work!
The event-only feature makes it possible to use a subscriber predicate that
accepts only a request argument within both multiple-interface subscriber
registrations and single-interface subscriber registrations. You needn't
make slightly different variations of predicates depending on the
subscription type arguments. Instead, just write all your subscriber
predicates so they only accept ``event`` in their ``__call__`` and they'll be
useful across all registrations for subscriptions that use an event as their
first argument, even ones which accept more than just ``event``.
However, the same caveat applies to predicates as to subscriber callables: if
you're subscribing to a multi-interface event, and the first interface is not
the event interface, the predicate won't work properly. In such a case,
you'll need to match the predicate ``__call__`` argument ordering and
composition to the ordering of the interfaces. For example, if the
registration for the subscription uses ``[SomeContext, SomeEvent]``, you'll
need to reflect that in the ordering of the parameters of the predicate's
``__call__`` method::
def __call__(self, context, event):
return event.request.path.startswith(self.val)
tl;dr: 1) When using multi-interface subscriptions, always use the event type
as the first subscription registration argument and 2) When 1 is true, use
only ``event`` in your subscriber and subscriber predicate parameter lists,
no matter how many interfaces the subscriber is notified with. This
combination will result in the maximum amount of reusability of subscriber
predicates and the least amount of thought on your part. Drink responsibly.
Bug Fixes
---------
- A failure when trying to locate the attribute ``__text__`` on route and view
predicates existed when the ``debug_routematch`` setting was true or when the
``pviews`` command was used. See https://github.com/Pylons/pyramid/pull/727
Documentation
-------------
- Sync up tutorial source files with the files that are rendered by the
scaffold that each uses.
1.4a4 (2012-11-14)
==================
Features
--------
- ``pyramid.authentication.AuthTktAuthenticationPolicy`` has been updated to
support newer hashing algorithms such as ``sha512``. Existing applications
should consider updating if possible for improved security over the default
md5 hashing.
- Added an ``effective_principals`` route and view predicate.
- Do not allow the userid returned from the ``authenticated_userid`` or the
userid that is one of the list of principals returned by
``effective_principals`` to be either of the strings ``system.Everyone`` or
``system.Authenticated`` when any of the built-in authorization policies that
live in ``pyramid.authentication`` are in use. These two strings are
reserved for internal usage by Pyramid and they will not be accepted as valid
userids.
- Slightly better debug logging from
``pyramid.authentication.RepozeWho1AuthenticationPolicy``.
- ``pyramid.security.view_execution_permitted`` used to return ``True`` if no
view could be found. It now raises a ``TypeError`` exception in that case, as
it doesn't make sense to assert that a nonexistent view is
execution-permitted. See https://github.com/Pylons/pyramid/issues/299.
- Allow a ``_depth`` argument to ``pyramid.view.view_config``, which will
permit limited composition reuse of the decorator by other software that
wants to provide custom decorators that are much like view_config.
- Allow an iterable of decorators to be passed to
``pyramid.config.Configurator.add_view``. This allows views to be wrapped
by more than one decorator without requiring combining the decorators
yourself.
Bug Fixes
---------
- In the past if a renderer returned ``None``, the body of the resulting
response would be set explicitly to the empty string. Instead, now, the body
is left unchanged, which allows the renderer to set a body itself by using
e.g. ``request.response.body = b'foo'``. The body set by the renderer will
be unmolested on the way out. See
https://github.com/Pylons/pyramid/issues/709
- In uncommon cases, the ``pyramid_excview_tween_factory`` might have
inadvertently raised a ``KeyError`` looking for ``request_iface`` as an
attribute of the request. It no longer fails in this case. See
https://github.com/Pylons/pyramid/issues/700
- Be more tolerant of potential error conditions in ``match_param`` and
``physical_path`` predicate implementations; instead of raising an exception,
return False.
- ``pyramid.view.render_view`` was not functioning properly under Python 3.x
due to a byte/unicode discrepancy. See
http://github.com/Pylons/pyramid/issues/721
Deprecations
------------
- ``pyramid.authentication.AuthTktAuthenticationPolicy`` will emit a warning if
an application is using the policy without explicitly passing a ``hashalg``
argument. This is because the default is "md5" which is considered
theoretically subject to collision attacks. If you really want "md5" then you
must specify it explicitly to get rid of the warning.
Documentation
-------------
- All of the tutorials that use
``pyramid.authentication.AuthTktAuthenticationPolicy`` now explicitly pass
``sha512`` as a ``hashalg`` argument.
Internals
---------
- Move ``TopologicalSorter`` from ``pyramid.config.util`` to ``pyramid.util``,
move ``CyclicDependencyError`` from ``pyramid.config.util`` to
``pyramid.exceptions``, rename ``Singleton`` to ``Sentinel`` and move from
``pyramid.config.util`` to ``pyramid.util``; this is in an effort to
move that stuff that may be an API one day out of ``pyramid.config.util``,
because that package should never be imported from non-Pyramid code.
TopologicalSorter is still not an API, but may become one.
- Get rid of shady monkeypatching of ``pyramid.request.Request`` and
``pyramid.response.Response`` done within the ``__init__.py`` of Pyramid.
Webob no longer relies on this being done. Instead, the ResponseClass
attribute of the Pyramid Request class is assigned to the Pyramid response
class; that's enough to satisfy WebOb and behave as it did before with the
monkeypatching.
1.4a3 (2012-10-26)
==================
Bug Fixes
---------
- The match_param predicate's text method was fixed to sort its values.
Part of https://github.com/Pylons/pyramid/pull/705
- 1.4a ``pyramid.scripting.prepare`` behaved differently than 1.3 series
function of same name. In particular, if passed a request, it would not
set the ``registry`` attribute of the request like 1.3 did. A symptom
would be that passing a request to ``pyramid.paster.bootstrap`` (which uses
the function) that did not have a ``registry`` attribute could assume that
the registry would be attached to the request by Pyramid. This assumption
could be made in 1.3, but not in 1.4. The assumption can now be made in
1.4 too (a registry is attached to a request passed to bootstrap or
prepare).
- When registering a view configuration that named a Chameleon ZPT renderer
with a macro name in it (e.g. ``renderer='some/template#somemacro.pt``) as
well as a view configuration without a macro name in it that pointed to the
same template (e.g. ``renderer='some/template.pt'``), internal caching could
confuse the two, and your code might have rendered one instead of the
other.
Features
--------
- Allow multiple values to be specified to the ``request_param`` view/route
predicate as a sequence. Previously only a single string value was allowed.
See https://github.com/Pylons/pyramid/pull/705
- Comments with references to documentation sections placed in scaffold
``.ini`` files.
- Added an HTTP Basic authentication policy
at ``pyramid.authentication.BasicAuthAuthenticationPolicy``.
- The Configurator ``testing_securitypolicy`` method now returns the policy
object it creates.
- The Configurator ``testing_securitypolicy`` method accepts two new
arguments: ``remember_result`` and ``forget_result``. If supplied, these
values influence the result of the policy's ``remember`` and ``forget``
methods, respectively.
- The DummySecurityPolicy created by ``testing_securitypolicy`` now sets a
``forgotten`` value on the policy (the value ``True``) when its ``forget``
method is called.
- The DummySecurityPolicy created by ``testing_securitypolicy`` now sets a
``remembered`` value on the policy, which is the value of the ``principal``
argument it's called with when its ``remember`` method is called.
- New ``physical_path`` view predicate. If specified, this value should be a
string or a tuple representing the physical traversal path of the context
found via traversal for this predicate to match as true. For example:
``physical_path='/'`` or ``physical_path='/a/b/c'`` or ``physical_path=('',
'a', 'b', 'c')``. This is not a path prefix match or a regex, it's a
whole-path match. It's useful when you want to always potentially show a
view when some object is traversed to, but you can't be sure about what kind
of object it will be, so you can't use the ``context`` predicate. The
individual path elements inbetween slash characters or in tuple elements
should be the Unicode representation of the name of the resource and should
not be encoded in any way.
1.4a2 (2012-09-27)
==================
Bug Fixes
---------
- When trying to determine Mako defnames and Chameleon macro names in asset
specifications, take into account that the filename may have a hyphen in
it. See https://github.com/Pylons/pyramid/pull/692
Features
--------
- A new ``pyramid.session.check_csrf_token`` convenience function was added.
- A ``check_csrf`` view predicate was added. For example, you can now do
``config.add_view(someview, check_csrf=True)``. When the predicate is
checked, if the ``csrf_token`` value in ``request.params`` matches the CSRF
token in the request's session, the view will be permitted to execute.
Otherwise, it will not be permitted to execute.
- Add ``Base.metadata.bind = engine`` to alchemy template, so that tables
defined imperatively will work.
Documentation
-------------
- update wiki2 SQLA tutorial with the changes required after inserting
``Base.metadata.bind = engine`` into the alchemy scaffold.
1.4a1 (2012-09-16)
==================
Bug Fixes
---------
- Forward port from 1.3 branch: When no authentication policy was configured,
a call to ``pyramid.security.effective_principals`` would unconditionally
return the empty list. This was incorrect, it should have unconditionally
returned ``[Everyone]``, and now does.
- Explicit url dispatch regexes can now contain colons.
https://github.com/Pylons/pyramid/issues/629
- On at least one 64-bit Ubuntu system under Python 3.2, using the
``view_config`` decorator caused a ``RuntimeError: dictionary changed size
during iteration`` exception. It no longer does. See
https://github.com/Pylons/pyramid/issues/635 for more information.
- In Mako Templates lookup, check if the uri is already adjusted and bring
it back to an asset spec. Normally occurs with inherited templates or
included components.
https://github.com/Pylons/pyramid/issues/606
https://github.com/Pylons/pyramid/issues/607
- In Mako Templates lookup, check for absolute uri (using mako directories)
when mixing up inheritance with asset specs.
https://github.com/Pylons/pyramid/issues/662
- HTTP Accept headers were not being normalized causing potentially
conflicting view registrations to go unnoticed. Two views that only
differ in the case ('text/html' vs. 'text/HTML') will now raise an error.
https://github.com/Pylons/pyramid/pull/620
- Forward-port from 1.3 branch: when registering multiple views with an
``accept`` predicate in a Pyramid application runing under Python 3, you
might have received a ``TypeError: unorderable types: function() <
function()`` exception.
Features
--------
- Configurator.add_directive now accepts arbitrary callables like partials or
objects implementing ``__call__`` which dont have ``__name__`` and
``__doc__`` attributes. See https://github.com/Pylons/pyramid/issues/621
and https://github.com/Pylons/pyramid/pull/647.
- Third-party custom view, route, and subscriber predicates can now be added
for use by view authors via
``pyramid.config.Configurator.add_view_predicate``,
``pyramid.config.Configurator.add_route_predicate`` and
``pyramid.config.Configurator.add_subscriber_predicate``. So, for example,
doing this::
config.add_view_predicate('abc', my.package.ABCPredicate)
Might allow a view author to do this in an application that configured that
predicate::
@view_config(abc=1)
Similar features exist for ``add_route``, and ``add_subscriber``. See
"Adding A Third Party View, Route, or Subscriber Predicate" in the Hooks
chapter for more information.
Note that changes made to support the above feature now means that only
actions registered using the same "order" can conflict with one another.
It used to be the case that actions registered at different orders could
potentially conflict, but to my knowledge nothing ever depended on this
behavior (it was a bit silly).
- Custom objects can be made easily JSON-serializable in Pyramid by defining
a ``__json__`` method on the object's class. This method should return
values natively serializable by ``json.dumps`` (such as ints, lists,
dictionaries, strings, and so forth).
- The JSON renderer now allows for the definition of custom type adapters to
convert unknown objects to JSON serializations.
- As of this release, the ``request_method`` predicate, when used, will also
imply that ``HEAD`` is implied when you use ``GET``. For example, using
``@view_config(request_method='GET')`` is equivalent to using
``@view_config(request_method=('GET', 'HEAD'))``. Using
``@view_config(request_method=('GET', 'POST')`` is equivalent to using
``@view_config(request_method=('GET', 'HEAD', 'POST')``. This is because
HEAD is a variant of GET that omits the body, and WebOb has special support
to return an empty body when a HEAD is used.
- ``config.add_request_method`` has been introduced to support extending
request objects with arbitrary callables. This method expands on the
previous ``config.set_request_property`` by supporting methods as well as
properties. This method now causes less code to be executed at
request construction time than ``config.set_request_property`` in
version 1.3.
- Don't add a ``?`` to URLs generated by ``request.resource_url`` if the
``query`` argument is provided but empty.
- Don't add a ``?`` to URLs generated by ``request.route_url`` if the
``_query`` argument is provided but empty.
- The static view machinery now raises (rather than returns) ``HTTPNotFound``
and ``HTTPMovedPermanently`` exceptions, so these can be caught by the
NotFound view (and other exception views).
- The Mako renderer now supports a def name in an asset spec. When the def
name is present in the asset spec, the system will render the template def
within the template and will return the result. An example asset spec is
``package:path/to/template#defname.mako``. This will render the def named
``defname`` inside the ``template.mako`` template instead of rendering the
entire template. The old way of returning a tuple in the form
``('defname', {})`` from the view is supported for backward compatibility,
- The Chameleon ZPT renderer now accepts a macro name in an asset spec. When
the macro name is present in the asset spec, the system will render the
macro listed as a ``define-macro`` and return the result instead of
rendering the entire template. An example asset spec:
``package:path/to/template#macroname.pt``. This will render the macro
defined as ``macroname`` within the ``template.pt`` template instead of the
entire templae.
- When there is a predicate mismatch exception (seen when no view matches for
a given request due to predicates not working), the exception now contains
a textual description of the predicate which didn't match.
- An ``add_permission`` directive method was added to the Configurator. This
directive registers a free-standing permission introspectable into the
Pyramid introspection system. Frameworks built atop Pyramid can thus use
the ``permissions`` introspectable category data to build a
comprehensive list of permissions supported by a running system. Before
this method was added, permissions were already registered in this
introspectable category as a side effect of naming them in an ``add_view``
call, this method just makes it possible to arrange for a permission to be
put into the ``permissions`` introspectable category without naming it
along with an associated view. Here's an example of usage of
``add_permission``::
config = Configurator()
config.add_permission('view')
- The ``UnencryptedCookieSessionFactoryConfig`` now accepts
``signed_serialize`` and ``signed_deserialize`` hooks which may be used
to influence how the sessions are marshalled (by default this is done
with HMAC+pickle).
- ``pyramid.testing.DummyRequest`` now supports methods supplied by the
``pyramid.util.InstancePropertyMixin`` class such as ``set_property``.
- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available to tweens.
- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available in the request object
returned from ``pyramid.paster.bootstrap``.
- ``request.context`` of environment request during ``bootstrap`` is now the
root object if a context isn't already set on a provided request.
- The ``pyramid.decorator.reify`` function is now an API, and was added to
the API documentation.
- Added the ``pyramid.testing.testConfig`` context manager, which can be used
to generate a configurator in a test, e.g. ``with testing.testConfig(...):``.
- Users can now invoke a subrequest from within view code using a new
``request.invoke_subrequest`` API.
Deprecations
------------
- The ``pyramid.config.Configurator.set_request_property`` has been
documentation-deprecated. The method remains usable but the more
featureful ``pyramid.config.Configurator.add_request_method`` should be
used in its place (it has all of the same capabilities but can also extend
the request object with methods).
Backwards Incompatibilities
---------------------------
- The Pyramid router no longer adds the values ``bfg.routes.route`` or
``bfg.routes.matchdict`` to the request's WSGI environment dictionary.
These values were docs-deprecated in ``repoze.bfg`` 1.0 (effectively seven
minor releases ago). If your code depended on these values, use
request.matched_route and request.matchdict instead.
- It is no longer possible to pass an environ dictionary directly to
``pyramid.traversal.ResourceTreeTraverser.__call__`` (aka
``ModelGraphTraverser.__call__``). Instead, you must pass a request
object. Passing an environment instead of a request has generated a
deprecation warning since Pyramid 1.1.
- Pyramid will no longer work properly if you use the
``webob.request.LegacyRequest`` as a request factory. Instances of the
LegacyRequest class have a ``request.path_info`` which return a string.
This Pyramid release assumes that ``request.path_info`` will
unconditionally be Unicode.
- The functions from ``pyramid.chameleon_zpt`` and ``pyramid.chameleon_text``
named ``get_renderer``, ``get_template``, ``render_template``, and
``render_template_to_response`` have been removed. These have issued a
deprecation warning upon import since Pyramid 1.0. Use
``pyramid.renderers.get_renderer()``,
``pyramid.renderers.get_renderer().implementation()``,
``pyramid.renderers.render()`` or ``pyramid.renderers.render_to_response``
respectively instead of these functions.
- The ``pyramid.configuration`` module was removed. It had been deprecated
since Pyramid 1.0 and printed a deprecation warning upon its use. Use
``pyramid.config`` instead.
- The ``pyramid.paster.PyramidTemplate`` API was removed. It had been
deprecated since Pyramid 1.1 and issued a warning on import. If your code
depended on this, adjust your code to import
``pyramid.scaffolds.PyramidTemplate`` instead.
- The ``pyramid.settings.get_settings()`` API was removed. It had been
printing a deprecation warning since Pyramid 1.0. If your code depended on
this API, use ``pyramid.threadlocal.get_current_registry().settings``
instead or use the ``settings`` attribute of the registry available from
the request (``request.registry.settings``).
- These APIs from the ``pyramid.testing`` module were removed. They have
been printing deprecation warnings since Pyramid 1.0:
* ``registerDummySecurityPolicy``, use
``pyramid.config.Configurator.testing_securitypolicy`` instead.
* ``registerResources`` (aka ``registerModels``, use
``pyramid.config.Configurator.testing_resources`` instead.
* ``registerEventListener``, use
``pyramid.config.Configurator.testing_add_subscriber`` instead.
* ``registerTemplateRenderer`` (aka `registerDummyRenderer``), use
``pyramid.config.Configurator.testing_add_template`` instead.
* ``registerView``, use ``pyramid.config.Configurator.add_view`` instead.
* ``registerUtility``, use
``pyramid.config.Configurator.registry.registerUtility`` instead.
* ``registerAdapter``, use
``pyramid.config.Configurator.registry.registerAdapter`` instead.
* ``registerSubscriber``, use
``pyramid.config.Configurator.add_subscriber`` instead.
* ``registerRoute``, use
``pyramid.config.Configurator.add_route`` instead.
* ``registerSettings``, use
``pyramid.config.Configurator.add_settings`` instead.
- In Pyramid 1.3 and previous, the ``__call__`` method of a Response object
was invoked before any finished callbacks were executed. As of this
release, the ``__call__`` method of a Response object is invoked *after*
finished callbacks are executed. This is in support of the
``request.invoke_subrequest`` feature.
Documentation
-------------
- Added an "Upgrading Pyramid" chapter to the narrative documentation. It
describes how to cope with deprecations and removals of Pyramid APIs and
how to show Pyramid-generated deprecation warnings while running tests and
while running a server.
- Added a "Invoking a Subrequest" chapter to the documentation. It describes
how to use the new ``request.invoke_subrequest`` API.
Dependencies
------------
- Pyramid now requires WebOb 1.2b3+ (the prior Pyramid release only relied on
1.2dev+). This is to ensure that we obtain a version of WebOb that returns
``request.path_info`` as text.
pyramid-1.4.5/rtd.txt 0000664 0001750 0001750 00000000050 12203712502 014001 0 ustar takaki takaki repoze.sphinx.autointerface
repoze.lru
pyramid-1.4.5/pyramid.egg-info/ 0000775 0001750 0001750 00000000000 12210157153 015616 5 ustar takaki takaki pyramid-1.4.5/pyramid.egg-info/top_level.txt 0000644 0001750 0001750 00000000010 12210157112 020330 0 ustar takaki takaki pyramid
pyramid-1.4.5/pyramid.egg-info/requires.txt 0000644 0001750 0001750 00000000476 12210157112 020216 0 ustar takaki takaki setuptools
Chameleon >= 1.2.3
Mako >= 0.3.6
WebOb >= 1.2b3
repoze.lru >= 0.4
zope.interface >= 3.8.0
zope.deprecation >= 3.5.0
venusian >= 1.0a3
translationstring >= 0.4
PasteDeploy >= 1.5.0
[docs]
Sphinx
docutils
repoze.sphinx.autointerface
[testing]
WebTest >= 1.3.1
zope.component>=3.11.0
nose
coverage
virtualenv pyramid-1.4.5/pyramid.egg-info/entry_points.txt 0000644 0001750 0001750 00000001375 12210157112 021113 0 ustar takaki takaki [pyramid.scaffold]
starter=pyramid.scaffolds:StarterProjectTemplate
zodb=pyramid.scaffolds:ZODBProjectTemplate
alchemy=pyramid.scaffolds:AlchemyProjectTemplate
[console_scripts]
bfg2pyramid = pyramid.fixers.fix_bfg_imports:main
pcreate = pyramid.scripts.pcreate:main
pserve = pyramid.scripts.pserve:main
pshell = pyramid.scripts.pshell:main
proutes = pyramid.scripts.proutes:main
pviews = pyramid.scripts.pviews:main
ptweens = pyramid.scripts.ptweens:main
prequest = pyramid.scripts.prequest:main
[paste.server_runner]
wsgiref = pyramid.scripts.pserve:wsgiref_server_runner
cherrypy = pyramid.scripts.pserve:cherrypy_server_runner
pyramid-1.4.5/pyramid.egg-info/SOURCES.txt 0000644 0001750 0001750 00000075266 12210157152 017517 0 ustar takaki takaki .gitignore
.gitmodules
.travis.yml
BFG_HISTORY.txt
CHANGES.txt
CONTRIBUTORS.txt
COPYRIGHT.txt
HACKING.txt
HISTORY.txt
LICENSE.txt
README.rst
RELEASING.txt
TODO.txt
rtd.txt
setup.cfg
setup.py
tox.ini
docs/.gitignore
docs/Makefile
docs/api.rst
docs/authorintro.rst
docs/changes.rst
docs/conf.py
docs/conventions.rst
docs/convert_images.sh
docs/copyright.rst
docs/coversizing.py
docs/designdefense.rst
docs/foreword.rst
docs/glossary.rst
docs/index.rst
docs/latexindex.rst
docs/make_book
docs/make_epub
docs/make_pdf
docs/python-3.png
docs/remake
docs/whatsnew-1.0.rst
docs/whatsnew-1.1.rst
docs/whatsnew-1.2.rst
docs/whatsnew-1.3.rst
docs/whatsnew-1.4.rst
docs/_static/latex-note.png
docs/_static/latex-warning.png
docs/api/authentication.rst
docs/api/authorization.rst
docs/api/compat.rst
docs/api/config.rst
docs/api/decorator.rst
docs/api/events.rst
docs/api/exceptions.rst
docs/api/httpexceptions.rst
docs/api/i18n.rst
docs/api/interfaces.rst
docs/api/location.rst
docs/api/paster.rst
docs/api/path.rst
docs/api/registry.rst
docs/api/renderers.rst
docs/api/request.rst
docs/api/response.rst
docs/api/scaffolds.rst
docs/api/scripting.rst
docs/api/security.rst
docs/api/session.rst
docs/api/settings.rst
docs/api/static.rst
docs/api/testing.rst
docs/api/threadlocal.rst
docs/api/traversal.rst
docs/api/tweens.rst
docs/api/url.rst
docs/api/view.rst
docs/api/wsgi.rst
docs/narr/advconfig.rst
docs/narr/assets.rst
docs/narr/commandline.rst
docs/narr/configuration.rst
docs/narr/environment.rst
docs/narr/events.rst
docs/narr/extconfig.rst
docs/narr/extending.rst
docs/narr/firstapp.rst
docs/narr/hellotraversal.py
docs/narr/hellotraversal.rst
docs/narr/helloworld.py
docs/narr/hooks.rst
docs/narr/hybrid.rst
docs/narr/i18n.rst
docs/narr/install.rst
docs/narr/introduction.rst
docs/narr/introspector.rst
docs/narr/logging.rst
docs/narr/muchadoabouttraversal.rst
docs/narr/paste.rst
docs/narr/project-debug.png
docs/narr/project.png
docs/narr/project.rst
docs/narr/renderers.rst
docs/narr/resources.rst
docs/narr/resourcetreetraverser.png
docs/narr/router.png
docs/narr/router.rst
docs/narr/scaffolding.rst
docs/narr/security.rst
docs/narr/sessions.rst
docs/narr/startup.rst
docs/narr/subrequest.rst
docs/narr/tb_introspector.png
docs/narr/templates.rst
docs/narr/testing.rst
docs/narr/threadlocals.rst
docs/narr/traversal.rst
docs/narr/upgrading.rst
docs/narr/urldispatch.rst
docs/narr/vhosting.rst
docs/narr/viewconfig.rst
docs/narr/views.rst
docs/narr/webob.rst
docs/narr/zca.rst
docs/narr/MyProject/CHANGES.txt
docs/narr/MyProject/MANIFEST.in
docs/narr/MyProject/README.txt
docs/narr/MyProject/development.ini
docs/narr/MyProject/production.ini
docs/narr/MyProject/setup.cfg
docs/narr/MyProject/setup.py
docs/narr/MyProject/myproject/__init__.py
docs/narr/MyProject/myproject/tests.py
docs/narr/MyProject/myproject/views.py
docs/narr/MyProject/myproject/static/favicon.ico
docs/narr/MyProject/myproject/static/footerbg.png
docs/narr/MyProject/myproject/static/headerbg.png
docs/narr/MyProject/myproject/static/ie6.css
docs/narr/MyProject/myproject/static/middlebg.png
docs/narr/MyProject/myproject/static/pylons.css
docs/narr/MyProject/myproject/static/pyramid-small.png
docs/narr/MyProject/myproject/static/pyramid.png
docs/narr/MyProject/myproject/static/transparent.gif
docs/narr/MyProject/myproject/templates/mytemplate.pt
docs/tutorials/.gitignore
docs/tutorials/bfg/index.rst
docs/tutorials/modwsgi/index.rst
docs/tutorials/wiki/NOTE-relocatable.txt
docs/tutorials/wiki/authorization.rst
docs/tutorials/wiki/background.rst
docs/tutorials/wiki/basiclayout.rst
docs/tutorials/wiki/definingmodels.rst
docs/tutorials/wiki/definingviews.rst
docs/tutorials/wiki/design.rst
docs/tutorials/wiki/distributing.rst
docs/tutorials/wiki/index.rst
docs/tutorials/wiki/installation.rst
docs/tutorials/wiki/tests.rst
docs/tutorials/wiki/src/authorization/CHANGES.txt
docs/tutorials/wiki/src/authorization/MANIFEST.in
docs/tutorials/wiki/src/authorization/README.txt
docs/tutorials/wiki/src/authorization/development.ini
docs/tutorials/wiki/src/authorization/production.ini
docs/tutorials/wiki/src/authorization/setup.cfg
docs/tutorials/wiki/src/authorization/setup.py
docs/tutorials/wiki/src/authorization/tutorial/__init__.py
docs/tutorials/wiki/src/authorization/tutorial/models.py
docs/tutorials/wiki/src/authorization/tutorial/security.py
docs/tutorials/wiki/src/authorization/tutorial/tests.py
docs/tutorials/wiki/src/authorization/tutorial/views.py
docs/tutorials/wiki/src/authorization/tutorial/static/favicon.ico
docs/tutorials/wiki/src/authorization/tutorial/static/footerbg.png
docs/tutorials/wiki/src/authorization/tutorial/static/headerbg.png
docs/tutorials/wiki/src/authorization/tutorial/static/ie6.css
docs/tutorials/wiki/src/authorization/tutorial/static/middlebg.png
docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
docs/tutorials/wiki/src/authorization/tutorial/static/pyramid-small.png
docs/tutorials/wiki/src/authorization/tutorial/static/pyramid.png
docs/tutorials/wiki/src/authorization/tutorial/static/transparent.gif
docs/tutorials/wiki/src/authorization/tutorial/templates/edit.pt
docs/tutorials/wiki/src/authorization/tutorial/templates/login.pt
docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
docs/tutorials/wiki/src/authorization/tutorial/templates/view.pt
docs/tutorials/wiki/src/basiclayout/CHANGES.txt
docs/tutorials/wiki/src/basiclayout/MANIFEST.in
docs/tutorials/wiki/src/basiclayout/README.txt
docs/tutorials/wiki/src/basiclayout/development.ini
docs/tutorials/wiki/src/basiclayout/production.ini
docs/tutorials/wiki/src/basiclayout/setup.cfg
docs/tutorials/wiki/src/basiclayout/setup.py
docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
docs/tutorials/wiki/src/basiclayout/tutorial/models.py
docs/tutorials/wiki/src/basiclayout/tutorial/tests.py
docs/tutorials/wiki/src/basiclayout/tutorial/views.py
docs/tutorials/wiki/src/basiclayout/tutorial/static/favicon.ico
docs/tutorials/wiki/src/basiclayout/tutorial/static/footerbg.png
docs/tutorials/wiki/src/basiclayout/tutorial/static/headerbg.png
docs/tutorials/wiki/src/basiclayout/tutorial/static/ie6.css
docs/tutorials/wiki/src/basiclayout/tutorial/static/middlebg.png
docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid-small.png
docs/tutorials/wiki/src/basiclayout/tutorial/static/pyramid.png
docs/tutorials/wiki/src/basiclayout/tutorial/static/transparent.gif
docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
docs/tutorials/wiki/src/models/CHANGES.txt
docs/tutorials/wiki/src/models/MANIFEST.in
docs/tutorials/wiki/src/models/README.txt
docs/tutorials/wiki/src/models/development.ini
docs/tutorials/wiki/src/models/production.ini
docs/tutorials/wiki/src/models/setup.cfg
docs/tutorials/wiki/src/models/setup.py
docs/tutorials/wiki/src/models/tutorial/__init__.py
docs/tutorials/wiki/src/models/tutorial/models.py
docs/tutorials/wiki/src/models/tutorial/tests.py
docs/tutorials/wiki/src/models/tutorial/views.py
docs/tutorials/wiki/src/models/tutorial/static/favicon.ico
docs/tutorials/wiki/src/models/tutorial/static/footerbg.png
docs/tutorials/wiki/src/models/tutorial/static/headerbg.png
docs/tutorials/wiki/src/models/tutorial/static/ie6.css
docs/tutorials/wiki/src/models/tutorial/static/middlebg.png
docs/tutorials/wiki/src/models/tutorial/static/pylons.css
docs/tutorials/wiki/src/models/tutorial/static/pyramid-small.png
docs/tutorials/wiki/src/models/tutorial/static/pyramid.png
docs/tutorials/wiki/src/models/tutorial/static/transparent.gif
docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
docs/tutorials/wiki/src/tests/CHANGES.txt
docs/tutorials/wiki/src/tests/MANIFEST.in
docs/tutorials/wiki/src/tests/README.txt
docs/tutorials/wiki/src/tests/development.ini
docs/tutorials/wiki/src/tests/production.ini
docs/tutorials/wiki/src/tests/setup.cfg
docs/tutorials/wiki/src/tests/setup.py
docs/tutorials/wiki/src/tests/tutorial/__init__.py
docs/tutorials/wiki/src/tests/tutorial/models.py
docs/tutorials/wiki/src/tests/tutorial/security.py
docs/tutorials/wiki/src/tests/tutorial/tests.py
docs/tutorials/wiki/src/tests/tutorial/views.py
docs/tutorials/wiki/src/tests/tutorial/static/favicon.ico
docs/tutorials/wiki/src/tests/tutorial/static/footerbg.png
docs/tutorials/wiki/src/tests/tutorial/static/headerbg.png
docs/tutorials/wiki/src/tests/tutorial/static/ie6.css
docs/tutorials/wiki/src/tests/tutorial/static/middlebg.png
docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
docs/tutorials/wiki/src/tests/tutorial/static/pyramid-small.png
docs/tutorials/wiki/src/tests/tutorial/static/pyramid.png
docs/tutorials/wiki/src/tests/tutorial/static/transparent.gif
docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
docs/tutorials/wiki/src/tests/tutorial/templates/login.pt
docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
docs/tutorials/wiki/src/views/CHANGES.txt
docs/tutorials/wiki/src/views/MANIFEST.in
docs/tutorials/wiki/src/views/README.txt
docs/tutorials/wiki/src/views/development.ini
docs/tutorials/wiki/src/views/production.ini
docs/tutorials/wiki/src/views/setup.cfg
docs/tutorials/wiki/src/views/setup.py
docs/tutorials/wiki/src/views/tutorial/__init__.py
docs/tutorials/wiki/src/views/tutorial/models.py
docs/tutorials/wiki/src/views/tutorial/tests.py
docs/tutorials/wiki/src/views/tutorial/views.py
docs/tutorials/wiki/src/views/tutorial/static/favicon.ico
docs/tutorials/wiki/src/views/tutorial/static/footerbg.png
docs/tutorials/wiki/src/views/tutorial/static/headerbg.png
docs/tutorials/wiki/src/views/tutorial/static/ie6.css
docs/tutorials/wiki/src/views/tutorial/static/middlebg.png
docs/tutorials/wiki/src/views/tutorial/static/pylons.css
docs/tutorials/wiki/src/views/tutorial/static/pyramid-small.png
docs/tutorials/wiki/src/views/tutorial/static/pyramid.png
docs/tutorials/wiki/src/views/tutorial/static/transparent.gif
docs/tutorials/wiki/src/views/tutorial/templates/edit.pt
docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
docs/tutorials/wiki/src/views/tutorial/templates/view.pt
docs/tutorials/wiki2/authorization.rst
docs/tutorials/wiki2/background.rst
docs/tutorials/wiki2/basiclayout.rst
docs/tutorials/wiki2/definingmodels.rst
docs/tutorials/wiki2/definingviews.rst
docs/tutorials/wiki2/design.rst
docs/tutorials/wiki2/distributing.rst
docs/tutorials/wiki2/index.rst
docs/tutorials/wiki2/installation.rst
docs/tutorials/wiki2/tests.rst
docs/tutorials/wiki2/src/authorization/CHANGES.txt
docs/tutorials/wiki2/src/authorization/MANIFEST.in
docs/tutorials/wiki2/src/authorization/README.txt
docs/tutorials/wiki2/src/authorization/development.ini
docs/tutorials/wiki2/src/authorization/production.ini
docs/tutorials/wiki2/src/authorization/setup.cfg
docs/tutorials/wiki2/src/authorization/setup.py
docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
docs/tutorials/wiki2/src/authorization/tutorial/models.py
docs/tutorials/wiki2/src/authorization/tutorial/security.py
docs/tutorials/wiki2/src/authorization/tutorial/tests.py
docs/tutorials/wiki2/src/authorization/tutorial/views.py
docs/tutorials/wiki2/src/authorization/tutorial/scripts/__init__.py
docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
docs/tutorials/wiki2/src/authorization/tutorial/static/favicon.ico
docs/tutorials/wiki2/src/authorization/tutorial/static/footerbg.png
docs/tutorials/wiki2/src/authorization/tutorial/static/headerbg.png
docs/tutorials/wiki2/src/authorization/tutorial/static/ie6.css
docs/tutorials/wiki2/src/authorization/tutorial/static/middlebg.png
docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid-small.png
docs/tutorials/wiki2/src/authorization/tutorial/static/pyramid.png
docs/tutorials/wiki2/src/authorization/tutorial/static/transparent.gif
docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt
docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt
docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt
docs/tutorials/wiki2/src/basiclayout/CHANGES.txt
docs/tutorials/wiki2/src/basiclayout/MANIFEST.in
docs/tutorials/wiki2/src/basiclayout/README.txt
docs/tutorials/wiki2/src/basiclayout/development.ini
docs/tutorials/wiki2/src/basiclayout/production.ini
docs/tutorials/wiki2/src/basiclayout/setup.cfg
docs/tutorials/wiki2/src/basiclayout/setup.py
docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/__init__.py
docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py
docs/tutorials/wiki2/src/basiclayout/tutorial/static/favicon.ico
docs/tutorials/wiki2/src/basiclayout/tutorial/static/footerbg.png
docs/tutorials/wiki2/src/basiclayout/tutorial/static/headerbg.png
docs/tutorials/wiki2/src/basiclayout/tutorial/static/ie6.css
docs/tutorials/wiki2/src/basiclayout/tutorial/static/middlebg.png
docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid-small.png
docs/tutorials/wiki2/src/basiclayout/tutorial/static/pyramid.png
docs/tutorials/wiki2/src/basiclayout/tutorial/static/transparent.gif
docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
docs/tutorials/wiki2/src/models/CHANGES.txt
docs/tutorials/wiki2/src/models/MANIFEST.in
docs/tutorials/wiki2/src/models/README.txt
docs/tutorials/wiki2/src/models/development.ini
docs/tutorials/wiki2/src/models/production.ini
docs/tutorials/wiki2/src/models/setup.cfg
docs/tutorials/wiki2/src/models/setup.py
docs/tutorials/wiki2/src/models/tutorial/__init__.py
docs/tutorials/wiki2/src/models/tutorial/models.py
docs/tutorials/wiki2/src/models/tutorial/tests.py
docs/tutorials/wiki2/src/models/tutorial/views.py
docs/tutorials/wiki2/src/models/tutorial/scripts/__init__.py
docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
docs/tutorials/wiki2/src/models/tutorial/static/favicon.ico
docs/tutorials/wiki2/src/models/tutorial/static/footerbg.png
docs/tutorials/wiki2/src/models/tutorial/static/headerbg.png
docs/tutorials/wiki2/src/models/tutorial/static/ie6.css
docs/tutorials/wiki2/src/models/tutorial/static/middlebg.png
docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
docs/tutorials/wiki2/src/models/tutorial/static/pyramid-small.png
docs/tutorials/wiki2/src/models/tutorial/static/pyramid.png
docs/tutorials/wiki2/src/models/tutorial/static/transparent.gif
docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
docs/tutorials/wiki2/src/tests/CHANGES.txt
docs/tutorials/wiki2/src/tests/MANIFEST.in
docs/tutorials/wiki2/src/tests/README.txt
docs/tutorials/wiki2/src/tests/development.ini
docs/tutorials/wiki2/src/tests/production.ini
docs/tutorials/wiki2/src/tests/setup.cfg
docs/tutorials/wiki2/src/tests/setup.py
docs/tutorials/wiki2/src/tests/tutorial/__init__.py
docs/tutorials/wiki2/src/tests/tutorial/models.py
docs/tutorials/wiki2/src/tests/tutorial/security.py
docs/tutorials/wiki2/src/tests/tutorial/tests.py
docs/tutorials/wiki2/src/tests/tutorial/views.py
docs/tutorials/wiki2/src/tests/tutorial/scripts/__init__.py
docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
docs/tutorials/wiki2/src/tests/tutorial/static/favicon.ico
docs/tutorials/wiki2/src/tests/tutorial/static/footerbg.png
docs/tutorials/wiki2/src/tests/tutorial/static/headerbg.png
docs/tutorials/wiki2/src/tests/tutorial/static/ie6.css
docs/tutorials/wiki2/src/tests/tutorial/static/middlebg.png
docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
docs/tutorials/wiki2/src/tests/tutorial/static/pyramid-small.png
docs/tutorials/wiki2/src/tests/tutorial/static/pyramid.png
docs/tutorials/wiki2/src/tests/tutorial/static/transparent.gif
docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt
docs/tutorials/wiki2/src/tests/tutorial/templates/login.pt
docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt
docs/tutorials/wiki2/src/views/CHANGES.txt
docs/tutorials/wiki2/src/views/MANIFEST.in
docs/tutorials/wiki2/src/views/README.txt
docs/tutorials/wiki2/src/views/development.ini
docs/tutorials/wiki2/src/views/production.ini
docs/tutorials/wiki2/src/views/setup.cfg
docs/tutorials/wiki2/src/views/setup.py
docs/tutorials/wiki2/src/views/tutorial/__init__.py
docs/tutorials/wiki2/src/views/tutorial/models.py
docs/tutorials/wiki2/src/views/tutorial/tests.py
docs/tutorials/wiki2/src/views/tutorial/views.py
docs/tutorials/wiki2/src/views/tutorial/scripts/__init__.py
docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
docs/tutorials/wiki2/src/views/tutorial/static/favicon.ico
docs/tutorials/wiki2/src/views/tutorial/static/footerbg.png
docs/tutorials/wiki2/src/views/tutorial/static/headerbg.png
docs/tutorials/wiki2/src/views/tutorial/static/ie6.css
docs/tutorials/wiki2/src/views/tutorial/static/middlebg.png
docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
docs/tutorials/wiki2/src/views/tutorial/static/pyramid-small.png
docs/tutorials/wiki2/src/views/tutorial/static/pyramid.png
docs/tutorials/wiki2/src/views/tutorial/static/transparent.gif
docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt
docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
docs/tutorials/wiki2/src/views/tutorial/templates/view.pt
pyramid/__init__.py
pyramid/asset.py
pyramid/authentication.py
pyramid/authorization.py
pyramid/chameleon_text.py
pyramid/chameleon_zpt.py
pyramid/compat.py
pyramid/decorator.py
pyramid/encode.py
pyramid/events.py
pyramid/exceptions.py
pyramid/httpexceptions.py
pyramid/i18n.py
pyramid/interfaces.py
pyramid/location.py
pyramid/mako_templating.py
pyramid/paster.py
pyramid/path.py
pyramid/registry.py
pyramid/renderers.py
pyramid/request.py
pyramid/resource.py
pyramid/response.py
pyramid/router.py
pyramid/scripting.py
pyramid/security.py
pyramid/session.py
pyramid/settings.py
pyramid/static.py
pyramid/testing.py
pyramid/threadlocal.py
pyramid/traversal.py
pyramid/tweens.py
pyramid/url.py
pyramid/urldispatch.py
pyramid/util.py
pyramid/view.py
pyramid/wsgi.py
pyramid.egg-info/PKG-INFO
pyramid.egg-info/SOURCES.txt
pyramid.egg-info/dependency_links.txt
pyramid.egg-info/entry_points.txt
pyramid.egg-info/not-zip-safe
pyramid.egg-info/requires.txt
pyramid.egg-info/top_level.txt
pyramid/config/__init__.py
pyramid/config/adapters.py
pyramid/config/assets.py
pyramid/config/factories.py
pyramid/config/i18n.py
pyramid/config/predicates.py
pyramid/config/rendering.py
pyramid/config/routes.py
pyramid/config/security.py
pyramid/config/settings.py
pyramid/config/testing.py
pyramid/config/tweens.py
pyramid/config/util.py
pyramid/config/views.py
pyramid/config/zca.py
pyramid/fixers/__init__.py
pyramid/fixers/fix_bfg_imports.py
pyramid/scaffolds/__init__.py
pyramid/scaffolds/copydir.py
pyramid/scaffolds/template.py
pyramid/scaffolds/tests.py
pyramid/scaffolds/alchemy/CHANGES.txt_tmpl
pyramid/scaffolds/alchemy/MANIFEST.in_tmpl
pyramid/scaffolds/alchemy/README.txt_tmpl
pyramid/scaffolds/alchemy/development.ini_tmpl
pyramid/scaffolds/alchemy/production.ini_tmpl
pyramid/scaffolds/alchemy/setup.cfg_tmpl
pyramid/scaffolds/alchemy/setup.py_tmpl
pyramid/scaffolds/alchemy/+package+/__init__.py
pyramid/scaffolds/alchemy/+package+/models.py
pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
pyramid/scaffolds/alchemy/+package+/views.py_tmpl
pyramid/scaffolds/alchemy/+package+/scripts/__init__.py
pyramid/scaffolds/alchemy/+package+/scripts/initializedb.py
pyramid/scaffolds/alchemy/+package+/static/favicon.ico
pyramid/scaffolds/alchemy/+package+/static/footerbg.png
pyramid/scaffolds/alchemy/+package+/static/headerbg.png
pyramid/scaffolds/alchemy/+package+/static/ie6.css
pyramid/scaffolds/alchemy/+package+/static/middlebg.png
pyramid/scaffolds/alchemy/+package+/static/pylons.css
pyramid/scaffolds/alchemy/+package+/static/pyramid-small.png
pyramid/scaffolds/alchemy/+package+/static/pyramid.png
pyramid/scaffolds/alchemy/+package+/static/transparent.gif
pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl
pyramid/scaffolds/starter/CHANGES.txt_tmpl
pyramid/scaffolds/starter/MANIFEST.in_tmpl
pyramid/scaffolds/starter/README.txt_tmpl
pyramid/scaffolds/starter/development.ini_tmpl
pyramid/scaffolds/starter/production.ini_tmpl
pyramid/scaffolds/starter/setup.cfg_tmpl
pyramid/scaffolds/starter/setup.py_tmpl
pyramid/scaffolds/starter/+package+/__init__.py
pyramid/scaffolds/starter/+package+/tests.py_tmpl
pyramid/scaffolds/starter/+package+/views.py_tmpl
pyramid/scaffolds/starter/+package+/static/favicon.ico
pyramid/scaffolds/starter/+package+/static/footerbg.png
pyramid/scaffolds/starter/+package+/static/headerbg.png
pyramid/scaffolds/starter/+package+/static/ie6.css
pyramid/scaffolds/starter/+package+/static/middlebg.png
pyramid/scaffolds/starter/+package+/static/pylons.css
pyramid/scaffolds/starter/+package+/static/pyramid-small.png
pyramid/scaffolds/starter/+package+/static/pyramid.png
pyramid/scaffolds/starter/+package+/static/transparent.gif
pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
pyramid/scaffolds/zodb/CHANGES.txt_tmpl
pyramid/scaffolds/zodb/MANIFEST.in_tmpl
pyramid/scaffolds/zodb/README.txt_tmpl
pyramid/scaffolds/zodb/development.ini_tmpl
pyramid/scaffolds/zodb/production.ini_tmpl
pyramid/scaffolds/zodb/setup.cfg_tmpl
pyramid/scaffolds/zodb/setup.py_tmpl
pyramid/scaffolds/zodb/+package+/__init__.py
pyramid/scaffolds/zodb/+package+/models.py
pyramid/scaffolds/zodb/+package+/tests.py_tmpl
pyramid/scaffolds/zodb/+package+/views.py_tmpl
pyramid/scaffolds/zodb/+package+/static/favicon.ico
pyramid/scaffolds/zodb/+package+/static/footerbg.png
pyramid/scaffolds/zodb/+package+/static/headerbg.png
pyramid/scaffolds/zodb/+package+/static/ie6.css
pyramid/scaffolds/zodb/+package+/static/middlebg.png
pyramid/scaffolds/zodb/+package+/static/pylons.css
pyramid/scaffolds/zodb/+package+/static/pyramid-small.png
pyramid/scaffolds/zodb/+package+/static/pyramid.png
pyramid/scaffolds/zodb/+package+/static/transparent.gif
pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt
pyramid/scripts/__init__.py
pyramid/scripts/common.py
pyramid/scripts/pcreate.py
pyramid/scripts/prequest.py
pyramid/scripts/proutes.py
pyramid/scripts/pserve.py
pyramid/scripts/pshell.py
pyramid/scripts/ptweens.py
pyramid/scripts/pviews.py
pyramid/tests/__init__.py
pyramid/tests/test_asset.py
pyramid/tests/test_authentication.py
pyramid/tests/test_authorization.py
pyramid/tests/test_chameleon_text.py
pyramid/tests/test_chameleon_zpt.py
pyramid/tests/test_decorator.py
pyramid/tests/test_docs.py
pyramid/tests/test_encode.py
pyramid/tests/test_events.py
pyramid/tests/test_exceptions.py
pyramid/tests/test_httpexceptions.py
pyramid/tests/test_i18n.py
pyramid/tests/test_integration.py
pyramid/tests/test_location.py
pyramid/tests/test_mako_templating.py
pyramid/tests/test_paster.py
pyramid/tests/test_path.py
pyramid/tests/test_registry.py
pyramid/tests/test_renderers.py
pyramid/tests/test_request.py
pyramid/tests/test_response.py
pyramid/tests/test_router.py
pyramid/tests/test_scripting.py
pyramid/tests/test_security.py
pyramid/tests/test_session.py
pyramid/tests/test_settings.py
pyramid/tests/test_static.py
pyramid/tests/test_testing.py
pyramid/tests/test_threadlocal.py
pyramid/tests/test_traversal.py
pyramid/tests/test_url.py
pyramid/tests/test_urldispatch.py
pyramid/tests/test_util.py
pyramid/tests/test_view.py
pyramid/tests/test_wsgi.py
pyramid/tests/fixtures/components.mak
pyramid/tests/fixtures/hello .world.mako
pyramid/tests/fixtures/hello_inherit_pkg.mak
pyramid/tests/fixtures/hellocompo.mak
pyramid/tests/fixtures/helloinherit.mak
pyramid/tests/fixtures/helloworld.mak
pyramid/tests/fixtures/helloworld.mako
pyramid/tests/fixtures/layout.mak
pyramid/tests/fixtures/minimal.pt
pyramid/tests/fixtures/minimal.txt
pyramid/tests/fixtures/nonminimal.mak
pyramid/tests/fixtures/nonminimal.txt
pyramid/tests/fixtures/pp.pt
pyramid/tests/fixtures/withmacro.pt
pyramid/tests/fixtures/static/.hiddenfile
pyramid/tests/fixtures/static/arcs.svg.tgz
pyramid/tests/fixtures/static/index.html
pyramid/tests/fixtures/static/subdir/index.html
pyramid/tests/pkgs/__init__.py
pyramid/tests/pkgs/ccbugapp/__init__.py
pyramid/tests/pkgs/conflictapp/__init__.py
pyramid/tests/pkgs/conflictapp/included.py
pyramid/tests/pkgs/defpermbugapp/__init__.py
pyramid/tests/pkgs/eventonly/__init__.py
pyramid/tests/pkgs/exceptionviewapp/__init__.py
pyramid/tests/pkgs/exceptionviewapp/models.py
pyramid/tests/pkgs/exceptionviewapp/views.py
pyramid/tests/pkgs/fixtureapp/__init__.py
pyramid/tests/pkgs/fixtureapp/models.py
pyramid/tests/pkgs/fixtureapp/views.py
pyramid/tests/pkgs/fixtureapp/subpackage/__init__.py
pyramid/tests/pkgs/fixtureapp/subpackage/templates/bar.pt
pyramid/tests/pkgs/fixtureapp/templates/fixture.pt
pyramid/tests/pkgs/forbiddenapp/__init__.py
pyramid/tests/pkgs/forbiddenview/__init__.py
pyramid/tests/pkgs/hybridapp/__init__.py
pyramid/tests/pkgs/hybridapp/views.py
pyramid/tests/pkgs/includeapp1/__init__.py
pyramid/tests/pkgs/includeapp1/root.py
pyramid/tests/pkgs/includeapp1/three.py
pyramid/tests/pkgs/includeapp1/two.py
pyramid/tests/pkgs/localeapp/__init__.py
pyramid/tests/pkgs/localeapp/locale/GARBAGE
pyramid/tests/pkgs/localeapp/locale/be/LC_MESSAGES
pyramid/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/localeapp/locale2/GARBAGE
pyramid/tests/pkgs/localeapp/locale2/be/LC_MESSAGES
pyramid/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/localeapp/locale3/GARBAGE
pyramid/tests/pkgs/localeapp/locale3/be/LC_MESSAGES
pyramid/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.mo
pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.po
pyramid/tests/pkgs/notfoundview/__init__.py
pyramid/tests/pkgs/permbugapp/__init__.py
pyramid/tests/pkgs/rendererscanapp/__init__.py
pyramid/tests/pkgs/rendererscanapp/one.pt
pyramid/tests/pkgs/rendererscanapp/two/__init__.py
pyramid/tests/pkgs/rendererscanapp/two/two.pt
pyramid/tests/pkgs/restbugapp/__init__.py
pyramid/tests/pkgs/restbugapp/views.py
pyramid/tests/pkgs/static_abspath/__init__.py
pyramid/tests/pkgs/static_assetspec/__init__.py
pyramid/tests/pkgs/static_routeprefix/__init__.py
pyramid/tests/pkgs/staticpermapp/__init__.py
pyramid/tests/pkgs/subrequestapp/__init__.py
pyramid/tests/pkgs/viewdecoratorapp/__init__.py
pyramid/tests/pkgs/viewdecoratorapp/views/__init__.py
pyramid/tests/pkgs/viewdecoratorapp/views/views.py
pyramid/tests/pkgs/viewdecoratorapp/views/templates/foo.pt
pyramid/tests/pkgs/wsgiapp2app/__init__.py
pyramid/tests/test_config/__init__.py
pyramid/tests/test_config/test_adapters.py
pyramid/tests/test_config/test_assets.py
pyramid/tests/test_config/test_factories.py
pyramid/tests/test_config/test_i18n.py
pyramid/tests/test_config/test_init.py
pyramid/tests/test_config/test_predicates.py
pyramid/tests/test_config/test_rendering.py
pyramid/tests/test_config/test_routes.py
pyramid/tests/test_config/test_security.py
pyramid/tests/test_config/test_settings.py
pyramid/tests/test_config/test_testing.py
pyramid/tests/test_config/test_tweens.py
pyramid/tests/test_config/test_util.py
pyramid/tests/test_config/test_views.py
pyramid/tests/test_config/files/minimal.pt
pyramid/tests/test_config/files/assets/dummy.txt
pyramid/tests/test_config/path/scanerror/__init__.py
pyramid/tests/test_config/path/scanerror/will_raise_error.py
pyramid/tests/test_config/pkgs/__init__.py
pyramid/tests/test_config/pkgs/asset/__init__.py
pyramid/tests/test_config/pkgs/asset/models.py
pyramid/tests/test_config/pkgs/asset/views.py
pyramid/tests/test_config/pkgs/asset/subpackage/__init__.py
pyramid/tests/test_config/pkgs/asset/subpackage/templates/bar.pt
pyramid/tests/test_config/pkgs/asset/templates/fixture.pt
pyramid/tests/test_config/pkgs/scanextrakw/__init__.py
pyramid/tests/test_config/pkgs/scannable/__init__.py
pyramid/tests/test_config/pkgs/scannable/another.py
pyramid/tests/test_config/pkgs/scannable/pod/notinit.py
pyramid/tests/test_config/pkgs/scannable/subpackage/__init__.py
pyramid/tests/test_config/pkgs/scannable/subpackage/notinit.py
pyramid/tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py
pyramid/tests/test_config/pkgs/selfscan/__init__.py
pyramid/tests/test_config/pkgs/selfscan/another.py
pyramid/tests/test_scaffolds/__init__.py
pyramid/tests/test_scaffolds/test_copydir.py
pyramid/tests/test_scaffolds/test_init.py
pyramid/tests/test_scaffolds/test_template.py
pyramid/tests/test_scaffolds/fixture_scaffold/CHANGES.txt_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/MANIFEST.in_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/README.txt_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/setup.cfg_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/setup.py_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/.badfile
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/__init__.py_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/resources.py
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/test_no_content.py_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/tests.py_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/views.py_tmpl
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/favicon.ico
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/footerbg.png
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/headerbg.png
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/ie6.css
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/middlebg.png
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/pylons.css
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/pyramid-small.png
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/pyramid.png
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/transparent.gif
pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl
pyramid/tests/test_scripts/__init__.py
pyramid/tests/test_scripts/dummy.py
pyramid/tests/test_scripts/test_common.py
pyramid/tests/test_scripts/test_pcreate.py
pyramid/tests/test_scripts/test_prequest.py
pyramid/tests/test_scripts/test_proutes.py
pyramid/tests/test_scripts/test_pserve.py
pyramid/tests/test_scripts/test_pshell.py
pyramid/tests/test_scripts/test_ptweens.py
pyramid/tests/test_scripts/test_pviews.py pyramid-1.4.5/pyramid.egg-info/not-zip-safe 0000644 0001750 0001750 00000000001 11701326317 020046 0 ustar takaki takaki
pyramid-1.4.5/pyramid.egg-info/PKG-INFO 0000644 0001750 0001750 00000121620 12210157112 016706 0 ustar takaki takaki Metadata-Version: 1.1
Name: pyramid
Version: 1.4.5
Summary: The Pyramid web application development framework, a Pylons project
Home-page: http://pylonsproject.org
Author: Chris McDonough, Agendaless Consulting
Author-email: pylons-discuss@googlegroups.com
License: BSD-derived (http://www.repoze.org/LICENSE.txt)
Description: Pyramid
=======
Pyramid is a small, fast, down-to-earth, open source Python web application
development framework. It makes real-world web application development and
deployment more fun, more predictable, and more productive.
Pyramid is produced by the `Pylons Project `_.
Support and Documentation
-------------------------
See the `Pylons Project website `_ to view
documentation, report bugs, and obtain support.
License
-------
Pyramid is offered under the BSD-derived `Repoze Public License
`_.
Authors
-------
Pyramid is made available by `Agendaless Consulting `_
and a team of contributors.
1.4.5 (201-08-30)
=================
Bug Fixes
---------
- The ``alchemy`` scaffold would break when the database was MySQL during
tables creation. See https://github.com/Pylons/pyramid/pull/1049. Backport
from master.
- It was not possible to use ``pyramid.httpexceptions.HTTPException`` as
the ``context`` of an exception view as very general catchall for
http-related exceptions when you wanted that exception view to override the
default exception view. See https://github.com/Pylons/pyramid/issues/985.
Backport from master.
- When the ``pyramid.reload_templates`` setting was true, and a Chameleon
template was reloaded, and the renderer specification named a macro
(e.g. ``foo#macroname.pt``), renderings of the template after the template
was reloaded due to a file change would produce the entire template body
instead of just a rendering of the macro. See
https://github.com/Pylons/pyramid/issues/1013. Backport from master.
- Fixed a Mako renderer bug returning a tuple with a previous defname value
in some circumstances. See https://github.com/Pylons/pyramid/issues/1037 for
more information. Backport from master.
- Make ``pserve.cherrypy_server_runner`` Python 3 compatible. See
https://github.com/Pylons/pyramid/issues/718. Backport from master.
1.4.4 (2013-08-27)
==================
Bug Fixes
---------
- Fix an obscure problem when combining a virtual root with a route with a
``*traverse`` in its pattern. Now the traversal path generated in
such a configuration will be correct, instead of an element missing
a leading slash.
1.4.3 (2013-07-18)
==================
Bug Fixes
---------
- ``pyramid.testing.DummyResource`` didn't define ``__bool__``, so code under
Python 3 would use ``__len__`` to find truthiness; this usually caused an
instance of DummyResource to be "falsy" instead of "truthy". See
https://github.com/Pylons/pyramid/pull/1032
1.4.2 (2013-05-21)
==================
Bug Fixes
---------
- ``mako_templating``: added defensive workaround for non-importability of
``mako`` due to upstream ``markupsafe`` dropping Python 3.2 support. Mako
templating will no longer work under the combination of MarkupSafe 0.17 and
Python 3.2 (although the combination of MarkupSafe 0.17 and Python 3.3 or any
supported Python 2 version will work OK).
- Make the ``pyramid.config.assets.PackageOverrides`` object implement the API
for ``__loader__`` objects specified in PEP 302. Proxies to the
``__loader__`` set by the importer, if present; otherwise, raises
``NotImplementedError``. This makes Pyramid static view overrides work
properly under Python 3.3 (previously they would not). See
https://github.com/Pylons/pyramid/pull/1015 for more information.
1.4.1 (2013-04-23)
==================
Bug Fixes
---------
- Spaces and dots may now be in mako renderer template paths. This was
broken when support for the new makodef syntax was added in 1.4a1.
See https://github.com/Pylons/pyramid/issues/950
- ``pyramid.debug_authorization=true`` will now correctly print out
``Allowed`` for views registered with ``NO_PERMISSION_REQUIRED`` instead
of invoking the ``permits`` method of the authorization policy.
See https://github.com/Pylons/pyramid/issues/954
- Pyramid failed to install on some systems due to being packaged with
some test files containing higher order characters in their names. These
files have now been removed. See
https://github.com/Pylons/pyramid/issues/981
1.4 (2012-12-18)
================
Docs
----
- Fix functional tests in the ZODB tutorial
1.4b3 (2012-12-10)
==================
- Packaging release only, no code changes. 1.4b2 was a brownbag release due to
missing directories in the tarball.
1.4b2 (2012-12-10)
==================
Docs
----
- Scaffolding is now PEP-8 compliant (at least for a brief shining moment).
- Tutorial improvements.
Backwards Incompatibilities
---------------------------
- Modified the ``_depth`` argument to ``pyramid.view.view_config`` to accept
a value relative to the invocation of ``view_config`` itself. Thus, when it
was previously expecting a value of ``1`` or greater, to reflect that
the caller of ``view_config`` is 1 stack frame away from ``venusian.attach``,
this implementation detail is now hidden.
- Modified the ``_backframes`` argument to ``pyramid.util.action_method`` in a
similar way to the changes described to ``_depth`` above. This argument
remains undocumented, but might be used in the wild by some insane person.
1.4b1 (2012-11-21)
==================
Features
--------
- Small microspeed enhancement which anticipates that a
``pyramid.response.Response`` object is likely to be returned from a view.
Some code is shortcut if the class of the object returned by a view is this
class. A similar microoptimization was done to
``pyramid.request.Request.is_response``.
- Make it possible to use variable arguments on ``p*`` commands (``pserve``,
``pshell``, ``pviews``, etc) in the form ``a=1 b=2`` so you can fill in
values in parameterized ``.ini`` file, e.g. ``pshell etc/development.ini
http_port=8080``. See https://github.com/Pylons/pyramid/pull/714
- A somewhat advanced and obscure feature of Pyramid event handlers is their
ability to handle "multi-interface" notifications. These notifications have
traditionally presented multiple objects to the subscriber callable. For
instance, if an event was sent by code like this::
registry.notify(event, context)
In the past, in order to catch such an event, you were obligated to write and
register an event subscriber that mentioned both the event and the context in
its argument list::
@subscriber([SomeEvent, SomeContextType])
def asubscriber(event, context):
pass
In many subscriber callables registered this way, it was common for the logic
in the subscriber callable to completely ignore the second and following
arguments (e.g. ``context`` in the above example might be ignored), because
they usually existed as attributes of the event anyway. You could usually
get the same value by doing ``event.context`` or similar.
The fact that you needed to put an extra argument which you usually ignored
in the subscriber callable body was only a minor annoyance until we added
"subscriber predicates", used to narrow the set of circumstances under which
a subscriber will be executed, in a prior 1.4 alpha release. Once those were
added, the annoyance was escalated, because subscriber predicates needed to
accept the same argument list and arity as the subscriber callables that they
were configured against. So, for example, if you had these two subscriber
registrations in your code::
@subscriber([SomeEvent, SomeContextType])
def asubscriber(event, context):
pass
@subscriber(SomeOtherEvent)
def asubscriber(event):
pass
And you wanted to use a subscriber predicate::
@subscriber([SomeEvent, SomeContextType], mypredicate=True)
def asubscriber1(event, context):
pass
@subscriber(SomeOtherEvent, mypredicate=True)
def asubscriber2(event):
pass
If an existing ``mypredicate`` subscriber predicate had been written in such
a way that it accepted only one argument in its ``__call__``, you could not
use it against a subscription which named more than one interface in its
subscriber interface list. Similarly, if you had written a subscriber
predicate that accepted two arguments, you couldn't use it against a
registration that named only a single interface type.
For example, if you created this predicate::
class MyPredicate(object):
# portions elided...
def __call__(self, event):
return self.val == event.context.foo
It would not work against a multi-interface-registered subscription, so in
the above example, when you attempted to use it against ``asubscriber1``, it
would fail at runtime with a TypeError, claiming something was attempting to
call it with too many arguments.
To hack around this limitation, you were obligated to design the
``mypredicate`` predicate to expect to receive in its ``__call__`` either a
single ``event`` argument (a SomeOtherEvent object) *or* a pair of arguments
(a SomeEvent object and a SomeContextType object), presumably by doing
something like this::
class MyPredicate(object):
# portions elided...
def __call__(self, event, context=None):
return self.val == event.context.foo
This was confusing and bad.
In order to allow people to ignore unused arguments to subscriber callables
and to normalize the relationship between event subscribers and subscriber
predicates, we now allow both subscribers and subscriber predicates to accept
only a single ``event`` argument even if they've been subscribed for
notifications that involve multiple interfaces. Subscribers and subscriber
predicates that accept only one argument will receive the first object passed
to ``notify``; this is typically (but not always) the event object. The
other objects involved in the subscription lookup will be discarded. You can
now write an event subscriber that accepts only ``event`` even if it
subscribes to multiple interfaces::
@subscriber([SomeEvent, SomeContextType])
def asubscriber(event):
# this will work!
This prevents you from needing to match the subscriber callable parameters to
the subscription type unnecessarily, especially when you don't make use of
any argument in your subscribers except for the event object itself.
Note, however, that if the event object is not the first
object in the call to ``notify``, you'll run into trouble. For example, if
notify is called with the context argument first::
registry.notify(context, event)
You won't be able to take advantage of the event-only feature. It will
"work", but the object received by your event handler won't be the event
object, it will be the context object, which won't be very useful::
@subscriber([SomeContextType, SomeEvent])
def asubscriber(event):
# bzzt! you'll be getting the context here as ``event``, and it'll
# be useless
Existing multiple-argument subscribers continue to work without issue, so you
should continue use those if your system notifies using multiple interfaces
and the first interface is not the event interface. For example::
@subscriber([SomeContextType, SomeEvent])
def asubscriber(context, event):
# this will still work!
The event-only feature makes it possible to use a subscriber predicate that
accepts only a request argument within both multiple-interface subscriber
registrations and single-interface subscriber registrations. You needn't
make slightly different variations of predicates depending on the
subscription type arguments. Instead, just write all your subscriber
predicates so they only accept ``event`` in their ``__call__`` and they'll be
useful across all registrations for subscriptions that use an event as their
first argument, even ones which accept more than just ``event``.
However, the same caveat applies to predicates as to subscriber callables: if
you're subscribing to a multi-interface event, and the first interface is not
the event interface, the predicate won't work properly. In such a case,
you'll need to match the predicate ``__call__`` argument ordering and
composition to the ordering of the interfaces. For example, if the
registration for the subscription uses ``[SomeContext, SomeEvent]``, you'll
need to reflect that in the ordering of the parameters of the predicate's
``__call__`` method::
def __call__(self, context, event):
return event.request.path.startswith(self.val)
tl;dr: 1) When using multi-interface subscriptions, always use the event type
as the first subscription registration argument and 2) When 1 is true, use
only ``event`` in your subscriber and subscriber predicate parameter lists,
no matter how many interfaces the subscriber is notified with. This
combination will result in the maximum amount of reusability of subscriber
predicates and the least amount of thought on your part. Drink responsibly.
Bug Fixes
---------
- A failure when trying to locate the attribute ``__text__`` on route and view
predicates existed when the ``debug_routematch`` setting was true or when the
``pviews`` command was used. See https://github.com/Pylons/pyramid/pull/727
Documentation
-------------
- Sync up tutorial source files with the files that are rendered by the
scaffold that each uses.
1.4a4 (2012-11-14)
==================
Features
--------
- ``pyramid.authentication.AuthTktAuthenticationPolicy`` has been updated to
support newer hashing algorithms such as ``sha512``. Existing applications
should consider updating if possible for improved security over the default
md5 hashing.
- Added an ``effective_principals`` route and view predicate.
- Do not allow the userid returned from the ``authenticated_userid`` or the
userid that is one of the list of principals returned by
``effective_principals`` to be either of the strings ``system.Everyone`` or
``system.Authenticated`` when any of the built-in authorization policies that
live in ``pyramid.authentication`` are in use. These two strings are
reserved for internal usage by Pyramid and they will not be accepted as valid
userids.
- Slightly better debug logging from
``pyramid.authentication.RepozeWho1AuthenticationPolicy``.
- ``pyramid.security.view_execution_permitted`` used to return ``True`` if no
view could be found. It now raises a ``TypeError`` exception in that case, as
it doesn't make sense to assert that a nonexistent view is
execution-permitted. See https://github.com/Pylons/pyramid/issues/299.
- Allow a ``_depth`` argument to ``pyramid.view.view_config``, which will
permit limited composition reuse of the decorator by other software that
wants to provide custom decorators that are much like view_config.
- Allow an iterable of decorators to be passed to
``pyramid.config.Configurator.add_view``. This allows views to be wrapped
by more than one decorator without requiring combining the decorators
yourself.
Bug Fixes
---------
- In the past if a renderer returned ``None``, the body of the resulting
response would be set explicitly to the empty string. Instead, now, the body
is left unchanged, which allows the renderer to set a body itself by using
e.g. ``request.response.body = b'foo'``. The body set by the renderer will
be unmolested on the way out. See
https://github.com/Pylons/pyramid/issues/709
- In uncommon cases, the ``pyramid_excview_tween_factory`` might have
inadvertently raised a ``KeyError`` looking for ``request_iface`` as an
attribute of the request. It no longer fails in this case. See
https://github.com/Pylons/pyramid/issues/700
- Be more tolerant of potential error conditions in ``match_param`` and
``physical_path`` predicate implementations; instead of raising an exception,
return False.
- ``pyramid.view.render_view`` was not functioning properly under Python 3.x
due to a byte/unicode discrepancy. See
http://github.com/Pylons/pyramid/issues/721
Deprecations
------------
- ``pyramid.authentication.AuthTktAuthenticationPolicy`` will emit a warning if
an application is using the policy without explicitly passing a ``hashalg``
argument. This is because the default is "md5" which is considered
theoretically subject to collision attacks. If you really want "md5" then you
must specify it explicitly to get rid of the warning.
Documentation
-------------
- All of the tutorials that use
``pyramid.authentication.AuthTktAuthenticationPolicy`` now explicitly pass
``sha512`` as a ``hashalg`` argument.
Internals
---------
- Move ``TopologicalSorter`` from ``pyramid.config.util`` to ``pyramid.util``,
move ``CyclicDependencyError`` from ``pyramid.config.util`` to
``pyramid.exceptions``, rename ``Singleton`` to ``Sentinel`` and move from
``pyramid.config.util`` to ``pyramid.util``; this is in an effort to
move that stuff that may be an API one day out of ``pyramid.config.util``,
because that package should never be imported from non-Pyramid code.
TopologicalSorter is still not an API, but may become one.
- Get rid of shady monkeypatching of ``pyramid.request.Request`` and
``pyramid.response.Response`` done within the ``__init__.py`` of Pyramid.
Webob no longer relies on this being done. Instead, the ResponseClass
attribute of the Pyramid Request class is assigned to the Pyramid response
class; that's enough to satisfy WebOb and behave as it did before with the
monkeypatching.
1.4a3 (2012-10-26)
==================
Bug Fixes
---------
- The match_param predicate's text method was fixed to sort its values.
Part of https://github.com/Pylons/pyramid/pull/705
- 1.4a ``pyramid.scripting.prepare`` behaved differently than 1.3 series
function of same name. In particular, if passed a request, it would not
set the ``registry`` attribute of the request like 1.3 did. A symptom
would be that passing a request to ``pyramid.paster.bootstrap`` (which uses
the function) that did not have a ``registry`` attribute could assume that
the registry would be attached to the request by Pyramid. This assumption
could be made in 1.3, but not in 1.4. The assumption can now be made in
1.4 too (a registry is attached to a request passed to bootstrap or
prepare).
- When registering a view configuration that named a Chameleon ZPT renderer
with a macro name in it (e.g. ``renderer='some/template#somemacro.pt``) as
well as a view configuration without a macro name in it that pointed to the
same template (e.g. ``renderer='some/template.pt'``), internal caching could
confuse the two, and your code might have rendered one instead of the
other.
Features
--------
- Allow multiple values to be specified to the ``request_param`` view/route
predicate as a sequence. Previously only a single string value was allowed.
See https://github.com/Pylons/pyramid/pull/705
- Comments with references to documentation sections placed in scaffold
``.ini`` files.
- Added an HTTP Basic authentication policy
at ``pyramid.authentication.BasicAuthAuthenticationPolicy``.
- The Configurator ``testing_securitypolicy`` method now returns the policy
object it creates.
- The Configurator ``testing_securitypolicy`` method accepts two new
arguments: ``remember_result`` and ``forget_result``. If supplied, these
values influence the result of the policy's ``remember`` and ``forget``
methods, respectively.
- The DummySecurityPolicy created by ``testing_securitypolicy`` now sets a
``forgotten`` value on the policy (the value ``True``) when its ``forget``
method is called.
- The DummySecurityPolicy created by ``testing_securitypolicy`` now sets a
``remembered`` value on the policy, which is the value of the ``principal``
argument it's called with when its ``remember`` method is called.
- New ``physical_path`` view predicate. If specified, this value should be a
string or a tuple representing the physical traversal path of the context
found via traversal for this predicate to match as true. For example:
``physical_path='/'`` or ``physical_path='/a/b/c'`` or ``physical_path=('',
'a', 'b', 'c')``. This is not a path prefix match or a regex, it's a
whole-path match. It's useful when you want to always potentially show a
view when some object is traversed to, but you can't be sure about what kind
of object it will be, so you can't use the ``context`` predicate. The
individual path elements inbetween slash characters or in tuple elements
should be the Unicode representation of the name of the resource and should
not be encoded in any way.
1.4a2 (2012-09-27)
==================
Bug Fixes
---------
- When trying to determine Mako defnames and Chameleon macro names in asset
specifications, take into account that the filename may have a hyphen in
it. See https://github.com/Pylons/pyramid/pull/692
Features
--------
- A new ``pyramid.session.check_csrf_token`` convenience function was added.
- A ``check_csrf`` view predicate was added. For example, you can now do
``config.add_view(someview, check_csrf=True)``. When the predicate is
checked, if the ``csrf_token`` value in ``request.params`` matches the CSRF
token in the request's session, the view will be permitted to execute.
Otherwise, it will not be permitted to execute.
- Add ``Base.metadata.bind = engine`` to alchemy template, so that tables
defined imperatively will work.
Documentation
-------------
- update wiki2 SQLA tutorial with the changes required after inserting
``Base.metadata.bind = engine`` into the alchemy scaffold.
1.4a1 (2012-09-16)
==================
Bug Fixes
---------
- Forward port from 1.3 branch: When no authentication policy was configured,
a call to ``pyramid.security.effective_principals`` would unconditionally
return the empty list. This was incorrect, it should have unconditionally
returned ``[Everyone]``, and now does.
- Explicit url dispatch regexes can now contain colons.
https://github.com/Pylons/pyramid/issues/629
- On at least one 64-bit Ubuntu system under Python 3.2, using the
``view_config`` decorator caused a ``RuntimeError: dictionary changed size
during iteration`` exception. It no longer does. See
https://github.com/Pylons/pyramid/issues/635 for more information.
- In Mako Templates lookup, check if the uri is already adjusted and bring
it back to an asset spec. Normally occurs with inherited templates or
included components.
https://github.com/Pylons/pyramid/issues/606
https://github.com/Pylons/pyramid/issues/607
- In Mako Templates lookup, check for absolute uri (using mako directories)
when mixing up inheritance with asset specs.
https://github.com/Pylons/pyramid/issues/662
- HTTP Accept headers were not being normalized causing potentially
conflicting view registrations to go unnoticed. Two views that only
differ in the case ('text/html' vs. 'text/HTML') will now raise an error.
https://github.com/Pylons/pyramid/pull/620
- Forward-port from 1.3 branch: when registering multiple views with an
``accept`` predicate in a Pyramid application runing under Python 3, you
might have received a ``TypeError: unorderable types: function() <
function()`` exception.
Features
--------
- Configurator.add_directive now accepts arbitrary callables like partials or
objects implementing ``__call__`` which dont have ``__name__`` and
``__doc__`` attributes. See https://github.com/Pylons/pyramid/issues/621
and https://github.com/Pylons/pyramid/pull/647.
- Third-party custom view, route, and subscriber predicates can now be added
for use by view authors via
``pyramid.config.Configurator.add_view_predicate``,
``pyramid.config.Configurator.add_route_predicate`` and
``pyramid.config.Configurator.add_subscriber_predicate``. So, for example,
doing this::
config.add_view_predicate('abc', my.package.ABCPredicate)
Might allow a view author to do this in an application that configured that
predicate::
@view_config(abc=1)
Similar features exist for ``add_route``, and ``add_subscriber``. See
"Adding A Third Party View, Route, or Subscriber Predicate" in the Hooks
chapter for more information.
Note that changes made to support the above feature now means that only
actions registered using the same "order" can conflict with one another.
It used to be the case that actions registered at different orders could
potentially conflict, but to my knowledge nothing ever depended on this
behavior (it was a bit silly).
- Custom objects can be made easily JSON-serializable in Pyramid by defining
a ``__json__`` method on the object's class. This method should return
values natively serializable by ``json.dumps`` (such as ints, lists,
dictionaries, strings, and so forth).
- The JSON renderer now allows for the definition of custom type adapters to
convert unknown objects to JSON serializations.
- As of this release, the ``request_method`` predicate, when used, will also
imply that ``HEAD`` is implied when you use ``GET``. For example, using
``@view_config(request_method='GET')`` is equivalent to using
``@view_config(request_method=('GET', 'HEAD'))``. Using
``@view_config(request_method=('GET', 'POST')`` is equivalent to using
``@view_config(request_method=('GET', 'HEAD', 'POST')``. This is because
HEAD is a variant of GET that omits the body, and WebOb has special support
to return an empty body when a HEAD is used.
- ``config.add_request_method`` has been introduced to support extending
request objects with arbitrary callables. This method expands on the
previous ``config.set_request_property`` by supporting methods as well as
properties. This method now causes less code to be executed at
request construction time than ``config.set_request_property`` in
version 1.3.
- Don't add a ``?`` to URLs generated by ``request.resource_url`` if the
``query`` argument is provided but empty.
- Don't add a ``?`` to URLs generated by ``request.route_url`` if the
``_query`` argument is provided but empty.
- The static view machinery now raises (rather than returns) ``HTTPNotFound``
and ``HTTPMovedPermanently`` exceptions, so these can be caught by the
NotFound view (and other exception views).
- The Mako renderer now supports a def name in an asset spec. When the def
name is present in the asset spec, the system will render the template def
within the template and will return the result. An example asset spec is
``package:path/to/template#defname.mako``. This will render the def named
``defname`` inside the ``template.mako`` template instead of rendering the
entire template. The old way of returning a tuple in the form
``('defname', {})`` from the view is supported for backward compatibility,
- The Chameleon ZPT renderer now accepts a macro name in an asset spec. When
the macro name is present in the asset spec, the system will render the
macro listed as a ``define-macro`` and return the result instead of
rendering the entire template. An example asset spec:
``package:path/to/template#macroname.pt``. This will render the macro
defined as ``macroname`` within the ``template.pt`` template instead of the
entire templae.
- When there is a predicate mismatch exception (seen when no view matches for
a given request due to predicates not working), the exception now contains
a textual description of the predicate which didn't match.
- An ``add_permission`` directive method was added to the Configurator. This
directive registers a free-standing permission introspectable into the
Pyramid introspection system. Frameworks built atop Pyramid can thus use
the ``permissions`` introspectable category data to build a
comprehensive list of permissions supported by a running system. Before
this method was added, permissions were already registered in this
introspectable category as a side effect of naming them in an ``add_view``
call, this method just makes it possible to arrange for a permission to be
put into the ``permissions`` introspectable category without naming it
along with an associated view. Here's an example of usage of
``add_permission``::
config = Configurator()
config.add_permission('view')
- The ``UnencryptedCookieSessionFactoryConfig`` now accepts
``signed_serialize`` and ``signed_deserialize`` hooks which may be used
to influence how the sessions are marshalled (by default this is done
with HMAC+pickle).
- ``pyramid.testing.DummyRequest`` now supports methods supplied by the
``pyramid.util.InstancePropertyMixin`` class such as ``set_property``.
- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available to tweens.
- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available in the request object
returned from ``pyramid.paster.bootstrap``.
- ``request.context`` of environment request during ``bootstrap`` is now the
root object if a context isn't already set on a provided request.
- The ``pyramid.decorator.reify`` function is now an API, and was added to
the API documentation.
- Added the ``pyramid.testing.testConfig`` context manager, which can be used
to generate a configurator in a test, e.g. ``with testing.testConfig(...):``.
- Users can now invoke a subrequest from within view code using a new
``request.invoke_subrequest`` API.
Deprecations
------------
- The ``pyramid.config.Configurator.set_request_property`` has been
documentation-deprecated. The method remains usable but the more
featureful ``pyramid.config.Configurator.add_request_method`` should be
used in its place (it has all of the same capabilities but can also extend
the request object with methods).
Backwards Incompatibilities
---------------------------
- The Pyramid router no longer adds the values ``bfg.routes.route`` or
``bfg.routes.matchdict`` to the request's WSGI environment dictionary.
These values were docs-deprecated in ``repoze.bfg`` 1.0 (effectively seven
minor releases ago). If your code depended on these values, use
request.matched_route and request.matchdict instead.
- It is no longer possible to pass an environ dictionary directly to
``pyramid.traversal.ResourceTreeTraverser.__call__`` (aka
``ModelGraphTraverser.__call__``). Instead, you must pass a request
object. Passing an environment instead of a request has generated a
deprecation warning since Pyramid 1.1.
- Pyramid will no longer work properly if you use the
``webob.request.LegacyRequest`` as a request factory. Instances of the
LegacyRequest class have a ``request.path_info`` which return a string.
This Pyramid release assumes that ``request.path_info`` will
unconditionally be Unicode.
- The functions from ``pyramid.chameleon_zpt`` and ``pyramid.chameleon_text``
named ``get_renderer``, ``get_template``, ``render_template``, and
``render_template_to_response`` have been removed. These have issued a
deprecation warning upon import since Pyramid 1.0. Use
``pyramid.renderers.get_renderer()``,
``pyramid.renderers.get_renderer().implementation()``,
``pyramid.renderers.render()`` or ``pyramid.renderers.render_to_response``
respectively instead of these functions.
- The ``pyramid.configuration`` module was removed. It had been deprecated
since Pyramid 1.0 and printed a deprecation warning upon its use. Use
``pyramid.config`` instead.
- The ``pyramid.paster.PyramidTemplate`` API was removed. It had been
deprecated since Pyramid 1.1 and issued a warning on import. If your code
depended on this, adjust your code to import
``pyramid.scaffolds.PyramidTemplate`` instead.
- The ``pyramid.settings.get_settings()`` API was removed. It had been
printing a deprecation warning since Pyramid 1.0. If your code depended on
this API, use ``pyramid.threadlocal.get_current_registry().settings``
instead or use the ``settings`` attribute of the registry available from
the request (``request.registry.settings``).
- These APIs from the ``pyramid.testing`` module were removed. They have
been printing deprecation warnings since Pyramid 1.0:
* ``registerDummySecurityPolicy``, use
``pyramid.config.Configurator.testing_securitypolicy`` instead.
* ``registerResources`` (aka ``registerModels``, use
``pyramid.config.Configurator.testing_resources`` instead.
* ``registerEventListener``, use
``pyramid.config.Configurator.testing_add_subscriber`` instead.
* ``registerTemplateRenderer`` (aka `registerDummyRenderer``), use
``pyramid.config.Configurator.testing_add_template`` instead.
* ``registerView``, use ``pyramid.config.Configurator.add_view`` instead.
* ``registerUtility``, use
``pyramid.config.Configurator.registry.registerUtility`` instead.
* ``registerAdapter``, use
``pyramid.config.Configurator.registry.registerAdapter`` instead.
* ``registerSubscriber``, use
``pyramid.config.Configurator.add_subscriber`` instead.
* ``registerRoute``, use
``pyramid.config.Configurator.add_route`` instead.
* ``registerSettings``, use
``pyramid.config.Configurator.add_settings`` instead.
- In Pyramid 1.3 and previous, the ``__call__`` method of a Response object
was invoked before any finished callbacks were executed. As of this
release, the ``__call__`` method of a Response object is invoked *after*
finished callbacks are executed. This is in support of the
``request.invoke_subrequest`` feature.
Documentation
-------------
- Added an "Upgrading Pyramid" chapter to the narrative documentation. It
describes how to cope with deprecations and removals of Pyramid APIs and
how to show Pyramid-generated deprecation warnings while running tests and
while running a server.
- Added a "Invoking a Subrequest" chapter to the documentation. It describes
how to use the new ``request.invoke_subrequest`` API.
Dependencies
------------
- Pyramid now requires WebOb 1.2b3+ (the prior Pyramid release only relied on
1.2dev+). This is to ensure that we obtain a version of WebOb that returns
``request.path_info`` as text.
Keywords: web wsgi pylons pyramid
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Framework :: Pyramid
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: License :: Repoze Public License
pyramid-1.4.5/pyramid.egg-info/dependency_links.txt 0000644 0001750 0001750 00000000001 12210157112 021655 0 ustar takaki takaki
pyramid-1.4.5/RELEASING.txt 0000664 0001750 0001750 00000004010 12203712502 014521 0 ustar takaki takaki Releasing Pyramid
=================
- Do any necessary branch merges (e.g. master to branch, branch to master).
- On release branch:
$ git pull
- Do platform test via tox:
$ tox -r
Make sure statement coverage is at 100%::
- Run Windows tests for Python 2.6, 2.7, 3.2, and 3.3 if feasible.
- Make sure all scaffold tests pass (Py 2.6, 2.7, 3.2, 3.3 and pypy on UNIX;
this doesn't work on Windows):
$ python pyramid/scaffolds/tests.py
- Ensure all features of the release are documented (audit CHANGES.txt or
communicate with contributors).
- Copy relevant changes (delta bug fixes) from CHANGES.txt to
docs/whatsnew-X.X (if it's a major release).
- Make sure docs render OK::
$ cd docs
$ make clean html
There should be no meaningful errors or warnings.
- Change setup.py version to the new version number.
- Change docs/conf.py version to the new version number.
- Change CHANGES.txt heading to reflect the new version number.
- Make sure PyPI long description renders (requires ``collective.dist``
installed into your Python)::
$ python setup.py check -r
- Create a release tag.
- Make sure your system Python has ``setuptools-git`` installed and release to
PyPI::
$ python setup.py sdist register upload
- Edit `http://wiki.python.org/moin/WebFrameworks
`_.
- Publish new version of docs.
- Announce to maillist.
- Announce to Twitter.
Announcement template
----------------------
Pyramid 1.1.X has been released.
Here are the changes:
<>
A "What's New In Pyramid 1.1" document exists at
http://docs.pylonsproject.org/projects/pyramid/1.1/whatsnew-1.1.html .
You will be able to see the 1.1 release documentation (across all
alphas and betas, as well as when it eventually gets to final release)
at http://docs.pylonsproject.org/projects/pyramid/1.1/ .
You can install it via PyPI:
easy_install Pyramid==1.1a4
Enjoy, and please report any issues you find to the issue tracker at
https://github.com/Pylons/pyramid/issues
Thanks!
- C
pyramid-1.4.5/README.rst 0000664 0001750 0001750 00000001316 12210154276 014152 0 ustar takaki takaki Pyramid
=======
Pyramid is a small, fast, down-to-earth, open source Python web application
development framework. It makes real-world web application development and
deployment more fun, more predictable, and more productive.
Pyramid is produced by the `Pylons Project `_.
Support and Documentation
-------------------------
See the `Pylons Project website `_ to view
documentation, report bugs, and obtain support.
License
-------
Pyramid is offered under the BSD-derived `Repoze Public License
`_.
Authors
-------
Pyramid is made available by `Agendaless Consulting `_
and a team of contributors.
pyramid-1.4.5/.travis.yml 0000664 0001750 0001750 00000000232 12210154276 014570 0 ustar takaki takaki language: python
python:
- 2.6
- 2.7
- pypy
- 3.2
script: python setup.py test
notifications:
email:
- pyramid-checkins@lists.repoze.org
pyramid-1.4.5/.gitignore 0000664 0001750 0001750 00000000334 12210154276 014452 0 ustar takaki takaki *.egg
*.egg-info
*.pyc
*$py.class
*.pt.py
*.txt.py
*~
.*.swp
.coverage
.tox/
nosetests.xml
pyramid/coverage.xml
tutorial.db
build/
dist/
bin/
lib/
include/
.idea/
distribute-*.tar.gz
bookenv/
jyenv/
pypyenv/
env*/
venv/
pyramid-1.4.5/TODO.txt 0000664 0001750 0001750 00000013524 12210154276 013775 0 ustar takaki takaki Pyramid TODOs
=============
Nice-to-Have
------------
- config.set_registry_attr with conflict detection... make sure the attr is
added before a commit, but register an action so a conflict can be detected.
- Provide the presumed renderer name to the called view as an attribute of
the request.
- Have action methods return their discriminators.
- Fix renderers chapter to better document system values passed to template
renderers.
- Modify view mapper narrative docs to not use pyramid_handlers.
- Modify the urldispatch chapter examples to assume a scan rather than
``add_view``.
- Introspection:
* ``default root factory`` category (prevent folks from needing to searh
"root factories" category)?
* ``default view mapper`` category (prevent folks from needing to search
"view mappers" category)?
* get rid of "tweens" category (can't sort properly?)
- Fix deployment recipes in cookbook (discourage proxying without changing
server).
- Try "with transaction.manager" in an exception view with SQLA (preempt
homina homina response about how to write "to the database" from within in
an exception view). Note: tried this and couldn't formulate the right
situation where the database could not be written to within an exception
view (but didn't try exhaustively).
- Add narrative docs for wsgiapp and wsgiapp2.
- Flesh out "Paste" narrative docs chapter.
- Basic WSGI documentation (pipeline / app / server).
- Change docs about creating a venusian decorator to not use ZCA (use
configurator methods instead).
- Try to better explain the relationship between a renderer and a template in
the templates chapter and elsewhere. Scan the documentation for reference
to a renderer as *only* view configuration (it's a larger concept now).
- Add better docs about what-to-do-when-behind-a-proxy: paste.urlmap ("/foo =
app1" and "domain app1.localhost = app1"), ProxyPreserveHost and the nginx
equivalent, preserving HTTPS URLs.
- Make "localizer" a property of request (instead of requiring
"get_localizer(request)"?
- Alias the stupid long default session factory name.
- Debug option to print view matching decision (e.g. debug_viewlookup or so).
- Non-bwcompat use of threadlocals that need to be documented or ameliorated:
security.principals_allowed_by_permission
resource.OverrideProvider._get_overrides: can't credibly be removed,
because it stores an overrideprovider as a module-scope global.
traversal.traverse: this API is a stepchild, and needs to be changed.
Configurator.add_translation_dirs: not passed any context but a message,
can't credibly be removed.
- Deprecate pyramid.security.view_execution_permitted (it only works for
traversal).
- Provide a ``has_view`` function.
- Update App engine chapter with less creaky directions.
- Idea from Zart:
diff --git a/pyramid/paster.py b/pyramid/paster.py
index b0e4d79..b3bd82a 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -8,6 +8,7 @@ from paste.deploy import (
from pyramid.compat import configparser
from logging.config import fileConfig
from pyramid.scripting import prepare
+from pyramid.config import Configurator
def get_app(config_uri, name=None, loadapp=loadapp):
""" Return the WSGI application named ``name`` in the PasteDeploy
@@ -111,3 +112,10 @@ def bootstrap(config_uri, request=None):
env['app'] = app
return env
+def make_pyramid_app(global_conf, app=None, **settings):
+ """Return pyramid application configured with provided settings"""
+ config = Configurator(package='pyramid', settings=settings)
+ if app:
+ config.include(app)
+ app = config.make_wsgi_app()
+ return app
diff --git a/setup.py b/setup.py
index 03ebb42..91e0e21 100644
--- a/setup.py
+++ b/setup.py
@@ -118,6 +118,8 @@ setup(name='pyramid',
[paste.server_runner]
wsgiref = pyramid.scripts.pserve:wsgiref_server_runner
cherrypy = pyramid.scripts.pserve:cherrypy_server_runner
+ [paster.app_factory]
+ main = pyramid.paster:make_pyramid_app
"""
)
Future
------
- 1.5: remove ``pyramid.view.static`` and ``pyramid.view.is_response``.
- 1.5: turn ``pyramid.settings.Settings`` into a function that returns the
original dict (after ``__getattr__`` deprecation period, it was deprecated
in 1.2).
- 1.5: Remove ``pyramid.requests.DeprecatedRequestMethodsMixin`` and code in
renderers module that looks for _response_content_type, et. al.
- 1.5: Maybe? deprecate set_request_property in favor of pointing people at
add_request_method, schedule removal for 1.8?
- 1.5: Remove pyramid.config.rendering set_renderer_globals_factory maybe.
- 1.5: remove pyramid.config.route _add_view_from_route function.
- 1.6: Remove IContextURL and TraversalContextURL.
- 1.7: Change ``pyramid.authentication.AuthTktAuthenticationPolicy`` default
``hashalg`` to ``sha512``.
Probably Bad Ideas
------------------
- Add functionality that mocks the behavior of ``repoze.browserid``.
- Consider implementing the API outlined in
http://plope.com/pyramid_auth_design_api_postmortem, phasing out the
current auth-n-auth abstractions in a backwards compatible way.
- Maybe add ``add_renderer_globals`` method to Configurator.
- Supply ``X-Vhm-Host`` support (probably better to do what paste#prefix
middleware does).
- Have ``remember`` and ``forget`` actually set headers on the response using
a response callback (and return the empty list)?
- http://pythonguy.wordpress.com/2011/06/22/dynamic-variables-revisited/
instead of thread locals
- Context manager for creating a new configurator (replacing
``with_package``). E.g.::
with config.partial(package='bar') as c:
c.add_view(...)
or::
with config.partial(introspection=False) as c:
c.add_view(..)
- _fix_registry should dictify the registry being fixed.
pyramid-1.4.5/tox.ini 0000664 0001750 0001750 00000000772 12203712502 013775 0 ustar takaki takaki [tox]
envlist =
py26,py27,py32,py33,pypy,cover
[testenv]
commands =
python setup.py dev
python setup.py test -q
[testenv:cover]
basepython =
python2.6
commands =
python setup.py dev
python setup.py nosetests --with-xunit --with-xcoverage
deps =
nosexcover
# we separate coverage into its own testenv because a) "last run wins" wrt
# cobertura jenkins reporting and b) pypy and jython can't handle any
# combination of versions of coverage and nosexcover that i can find.
pyramid-1.4.5/setup.cfg 0000664 0001750 0001750 00000000434 12210157153 014301 0 ustar takaki takaki [easy_install]
zip_ok = false
[nosetests]
match = ^test
where = pyramid
nocapture = 1
cover-package = pyramid
cover-erase = 1
[aliases]
dev = develop easy_install pyramid[testing]
docs = develop easy_install pyramid[docs]
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
pyramid-1.4.5/pyramid/ 0000775 0001750 0001750 00000000000 12210157153 014124 5 ustar takaki takaki pyramid-1.4.5/pyramid/renderers.py 0000664 0001750 0001750 00000056575 12210154276 016514 0 ustar takaki takaki import json
import os
import re
import pkg_resources
import threading
from zope.interface import (
implementer,
providedBy,
)
from zope.interface.registry import Components
from pyramid.interfaces import (
IChameleonLookup,
IChameleonTranslate,
IJSONAdapter,
IRendererGlobalsFactory,
IRendererFactory,
IResponseFactory,
ITemplateRenderer,
IRendererInfo,
)
from pyramid.asset import asset_spec_from_abspath
from pyramid.compat import (
string_types,
text_type,
)
from pyramid.decorator import reify
from pyramid.events import BeforeRender
from pyramid.path import (
caller_package,
package_path,
)
from pyramid.response import Response
from pyramid.threadlocal import get_current_registry
# API
def render(renderer_name, value, request=None, package=None):
""" Using the renderer specified as ``renderer_name`` (a template
or a static renderer) render the value (or set of values) present
in ``value``. Return the result of the renderer's ``__call__``
method (usually a string or Unicode).
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:`asset specification`
(e.g. ``packagename:path/to/template.pt``).
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 asset specification by
combining the package supplied as ``package`` with the relative
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.
The ``value`` provided will be supplied as the input to the
renderer. Usually, for template renderings, this should be a
dictionary. For other renderers, this will need to be whatever
sort of value the renderer expects.
The 'system' values supplied to the renderer will include a basic set of
top-level system names, such as ``request``, ``context``,
``renderer_name``, and ``view``. See :ref:`renderer_system_values` for
the full list. If :term:`renderer globals` have been specified, these
will also be used to agument the value.
Supply a ``request`` parameter in order to provide the renderer
with the most correct 'system' values (``request`` and ``context``
in particular).
"""
try:
registry = request.registry
except AttributeError:
registry = None
if package is None:
package = caller_package()
helper = RendererHelper(name=renderer_name, package=package,
registry=registry)
return helper.render(value, None, request=request)
def render_to_response(renderer_name, value, request=None, package=None):
""" Using the renderer specified as ``renderer_name`` (a template
or a static renderer) render the value (or set of values) using
the result of the renderer's ``__call__`` method (usually a string
or Unicode) as the response body.
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:`asset specification`.
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 asset specification by
combining the package supplied as ``package`` with the relative
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.
The ``value`` provided will be supplied as the input to the
renderer. Usually, for template renderings, this should be a
dictionary. For other renderers, this will need to be whatever
sort of value the renderer expects.
The 'system' values supplied to the renderer will include a basic set of
top-level system names, such as ``request``, ``context``,
``renderer_name``, and ``view``. See :ref:`renderer_system_values` for
the full list. If :term:`renderer globals` have been specified, these
will also be used to agument the value.
Supply a ``request`` parameter in order to provide the renderer
with the most correct 'system' values (``request`` and ``context``
in particular). Keep in mind that if the ``request`` parameter is
not passed in, any changes to ``request.response`` attributes made
before calling this function will be ignored.
"""
try:
registry = request.registry
except AttributeError:
registry = None
if package is None:
package = caller_package()
helper = RendererHelper(name=renderer_name, package=package,
registry=registry)
return helper.render_to_response(value, None, request=request)
def get_renderer(renderer_name, package=None):
""" Return the renderer object for the renderer named as
``renderer_name``.
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 asset specification by
combining the package supplied as ``package`` with the relative
asset specification supplied as ``renderer_name``. If you do
not supply a ``package`` (or ``package`` is ``None``) the package
name of the *caller* of this function will be used as the package.
"""
if package is None:
package = caller_package()
helper = RendererHelper(name=renderer_name, package=package)
return helper.renderer
# concrete renderer factory implementations (also API)
def string_renderer_factory(info):
def _render(value, system):
if not isinstance(value, string_types):
value = str(value)
request = system.get('request')
if request is not None:
response = request.response
ct = response.content_type
if ct == response.default_content_type:
response.content_type = 'text/plain'
return value
return _render
_marker = object()
class JSON(object):
""" Renderer that returns a JSON-encoded string.
Configure a custom JSON renderer using the
:meth:`~pyramid.config.Configurator.add_renderer` API at application
startup time:
.. code-block:: python
from pyramid.config import Configurator
config = Configurator()
config.add_renderer('myjson', JSON(indent=4))
Once this renderer is registered as above, you can use
``myjson`` as the ``renderer=`` parameter to ``@view_config`` or
:meth:`~pyramid.config.Configurator.add_view``:
.. code-block:: python
from pyramid.view import view_config
@view_config(renderer='myjson')
def myview(request):
return {'greeting':'Hello world'}
Custom objects can be serialized using the renderer by either
implementing the ``__json__`` magic method, or by registering
adapters with the renderer. See
:ref:`json_serializing_custom_objects` for more information.
The default serializer uses ``json.JSONEncoder``. A different
serializer can be specified via the ``serializer`` argument.
Custom serializers should accept the object, a callback
``default``, and any extra ``kw`` keyword argments passed during
renderer construction.
.. note::
This feature is new in Pyramid 1.4. Prior to 1.4 there was
no public API for supplying options to the underlying
serializer without defining a custom renderer.
"""
def __init__(self, serializer=json.dumps, adapters=(), **kw):
""" Any keyword arguments will be passed to the ``serializer``
function."""
self.serializer = serializer
self.kw = kw
self.components = Components()
for type, adapter in adapters:
self.add_adapter(type, adapter)
def add_adapter(self, type_or_iface, adapter):
""" When an object of the type (or interface) ``type_or_iface`` fails
to automatically encode using the serializer, the renderer will use
the adapter ``adapter`` to convert it into a JSON-serializable
object. The adapter must accept two arguments: the object and the
currently active request.
.. code-block:: python
class Foo(object):
x = 5
def foo_adapter(obj, request):
return obj.x
renderer = JSON(indent=4)
renderer.add_adapter(Foo, foo_adapter)
When you've done this, the JSON renderer will be able to serialize
instances of the ``Foo`` class when they're encountered in your view
results."""
self.components.registerAdapter(adapter, (type_or_iface,),
IJSONAdapter)
def __call__(self, info):
""" Returns a plain JSON-encoded string with content-type
``application/json``. The content-type may be overridden by
setting ``request.response.content_type``."""
def _render(value, system):
request = system.get('request')
if request is not None:
response = request.response
ct = response.content_type
if ct == response.default_content_type:
response.content_type = 'application/json'
default = self._make_default(request)
return self.serializer(value, default=default, **self.kw)
return _render
def _make_default(self, request):
def default(obj):
if hasattr(obj, '__json__'):
return obj.__json__(request)
obj_iface = providedBy(obj)
adapters = self.components.adapters
result = adapters.lookup((obj_iface,), IJSONAdapter,
default=_marker)
if result is _marker:
raise TypeError('%r is not JSON serializable' % (obj,))
return result(obj, request)
return default
json_renderer_factory = JSON() # bw compat
class JSONP(JSON):
""" `JSONP `_ renderer factory helper
which implements a hybrid json/jsonp renderer. JSONP is useful for
making cross-domain AJAX requests.
Configure a JSONP renderer using the
:meth:`pyramid.config.Configurator.add_renderer` API at application
startup time:
.. code-block:: python
from pyramid.config import Configurator
config = Configurator()
config.add_renderer('jsonp', JSONP(param_name='callback'))
The class' constructor also accepts arbitrary keyword arguments. All
keyword arguments except ``param_name`` are passed to the ``json.dumps``
function as its keyword arguments.
.. code-block:: python
from pyramid.config import Configurator
config = Configurator()
config.add_renderer('jsonp', JSONP(param_name='callback', indent=4))
.. note:: The ability of this class to accept a ``**kw`` in its
constructor is new as of Pyramid 1.4.
The arguments passed to this class' constructor mean the same thing as
the arguments passed to :class:`pyramid.renderers.JSON` (including
``serializer`` and ``adapters``).
Once this renderer is registered via
:meth:`~pyramid.config.Configurator.add_renderer` as above, you can use
``jsonp`` as the ``renderer=`` parameter to ``@view_config`` or
:meth:`pyramid.config.Configurator.add_view``:
.. code-block:: python
from pyramid.view import view_config
@view_config(renderer='jsonp')
def myview(request):
return {'greeting':'Hello world'}
When a view is called that uses the JSONP renderer:
- If there is a parameter in the request's HTTP query string that matches
the ``param_name`` of the registered JSONP renderer (by default,
``callback``), the renderer will return a JSONP response.
- If there is no callback parameter in the request's query string, the
renderer will return a 'plain' JSON response.
.. note:: This feature is new in Pyramid 1.1.
See also: :ref:`jsonp_renderer`.
"""
def __init__(self, param_name='callback', **kw):
self.param_name = param_name
JSON.__init__(self, **kw)
def __call__(self, info):
""" Returns JSONP-encoded string with content-type
``application/javascript`` if query parameter matching
``self.param_name`` is present in request.GET; otherwise returns
plain-JSON encoded string with content-type ``application/json``"""
def _render(value, system):
request = system['request']
default = self._make_default(request)
val = self.serializer(value, default=default, **self.kw)
callback = request.GET.get(self.param_name)
if callback is None:
ct = 'application/json'
body = val
else:
ct = 'application/javascript'
body = '%s(%s)' % (callback, val)
response = request.response
if response.content_type == response.default_content_type:
response.content_type = ct
return body
return _render
# utility functions, not API
@implementer(IChameleonLookup)
class ChameleonRendererLookup(object):
spec_re = re.compile(
r'(?P[\w_.:/-]+)'
r'(?:\#(?P[\w_]+))?'
r'(\.(?P.*))'
)
def __init__(self, impl, registry):
self.impl = impl
self.registry = registry
self.lock = threading.Lock()
def get_spec(self, name, package):
if not package:
# if there's no package, we can't do any conversion
return name
spec = name
isabspath = os.path.isabs(name)
colon_in_name = ':' in name
isabsspec = colon_in_name and (not isabspath)
isrelspec = (not isabsspec) and (not isabspath)
# if it's already an absolute spec, we don't need to do anything,
# but if it's a relative spec or an absolute path, we need to try
# to convert it to an absolute spec
if isrelspec:
# convert relative asset spec to absolute asset spec
pp = package_path(package)
spec = os.path.join(pp, spec)
spec = asset_spec_from_abspath(spec, package)
elif isabspath:
# convert absolute path to absolute asset spec
spec = asset_spec_from_abspath(spec, package)
return spec
@property # wait until completely necessary to look up translator
def translate(self):
return self.registry.queryUtility(IChameleonTranslate)
@property # wait until completely necessary to look up debug_templates
def debug(self):
settings = self.registry.settings
if settings is None:
return False
return settings.get('debug_templates', False)
@property # wait until completely necessary to look up reload_templates
def auto_reload(self):
settings = self.registry.settings
if settings is None:
return False
return settings.get('reload_templates', False)
def _crack_spec(self, spec):
asset, macro, ext = self.spec_re.match(spec).group(
'asset', 'defname', 'ext'
)
return asset, macro, ext
def __call__(self, info):
spec = self.get_spec(info.name, info.package)
registry = info.registry
if os.path.isabs(spec):
# 'spec' is an absolute filename
if not os.path.exists(spec):
raise ValueError('Missing template file: %s' % spec)
renderer = registry.queryUtility(ITemplateRenderer, name=spec)
if renderer is None:
renderer = self.impl(spec, self, macro=None)
# cache the template
with self.lock:
registry.registerUtility(renderer,
ITemplateRenderer, name=spec)
else:
# spec is a package:relpath asset spec
renderer = registry.queryUtility(ITemplateRenderer, name=spec)
if renderer is None:
asset, macro, ext = self._crack_spec(spec)
spec_without_macro = '%s.%s' % (asset, ext)
try:
package_name, filename = spec_without_macro.split(':', 1)
except ValueError: # pragma: no cover
# somehow we were passed a relative pathname; this
# should die
package_name = caller_package(4).__name__
filename = spec_without_macro
abspath = pkg_resources.resource_filename(package_name,
filename)
if not pkg_resources.resource_exists(package_name, filename):
raise ValueError(
'Missing template asset: %s (%s)' % (
spec_without_macro, abspath)
)
renderer = self.impl(abspath, self, macro=macro)
settings = info.settings
if not settings.get('reload_assets'):
# cache the template
with self.lock:
registry.registerUtility(renderer, ITemplateRenderer,
name=spec)
return renderer
registry_lock = threading.Lock()
def template_renderer_factory(info, impl, lock=registry_lock):
registry = info.registry
lookup = registry.queryUtility(IChameleonLookup, name=info.type)
if lookup is None:
lookup = ChameleonRendererLookup(impl, registry)
with lock:
registry.registerUtility(lookup, IChameleonLookup, name=info.type)
return lookup(info)
@implementer(IRendererInfo)
class RendererHelper(object):
def __init__(self, name=None, package=None, registry=None):
if name and '.' in name:
rtype = os.path.splitext(name)[1]
else:
# important.. must be a string; cannot be None; see issue 249
rtype = name or ''
if registry is None:
registry = get_current_registry()
self.name = name
self.package = package
self.type = rtype
self.registry = registry
@reify
def settings(self):
settings = self.registry.settings
if settings is None:
settings = {}
return settings
@reify
def renderer(self):
factory = self.registry.queryUtility(IRendererFactory, name=self.type)
if factory is None:
raise ValueError(
'No such renderer factory %s' % str(self.type))
return factory(self)
def get_renderer(self):
return self.renderer
def render_view(self, request, response, view, context):
system = {'view':view,
'renderer_name':self.name, # b/c
'renderer_info':self,
'context':context,
'request':request,
'req':request,
}
return self.render_to_response(response, system, request=request)
def render(self, value, system_values, request=None):
renderer = self.renderer
if system_values is None:
system_values = {
'view':None,
'renderer_name':self.name, # b/c
'renderer_info':self,
'context':getattr(request, 'context', None),
'request':request,
'req':request,
}
system_values = BeforeRender(system_values, value)
registry = self.registry
globals_factory = registry.queryUtility(IRendererGlobalsFactory)
if globals_factory is not None:
renderer_globals = globals_factory(system_values)
if renderer_globals:
system_values.update(renderer_globals)
registry.notify(system_values)
result = renderer(value, system_values)
return result
def render_to_response(self, value, system_values, request=None):
result = self.render(value, system_values, request=request)
return self._make_response(result, request)
def _make_response(self, result, request):
# broken out of render_to_response as a separate method for testing
# purposes
response = getattr(request, 'response', None)
if response is None:
# request is None or request is not a pyramid.response.Response
registry = self.registry
response_factory = registry.queryUtility(IResponseFactory,
default=Response)
response = response_factory()
if result is not None:
if isinstance(result, text_type):
response.text = result
else:
response.body = result
if request is not None:
# deprecated mechanism to set up request.response_* attrs, see
# pyramid.request.Request
attrs = request.__dict__
content_type = attrs.get('_response_content_type', None)
if content_type is not None:
response.content_type = content_type
headerlist = attrs.get('_response_headerlist', None)
if headerlist is not None:
for k, v in headerlist:
response.headers.add(k, v)
status = attrs.get('_response_status', None)
if status is not None:
response.status = status
charset = attrs.get('_response_charset', None)
if charset is not None:
response.charset = charset
cache_for = attrs.get('_response_cache_for', None)
if cache_for is not None:
response.cache_expires = cache_for
return response
def clone(self, name=None, package=None, registry=None):
if name is None:
name = self.name
if package is None:
package = self.package
if registry is None:
registry = self.registry
return self.__class__(name=name, package=package, registry=registry)
class NullRendererHelper(RendererHelper):
""" Special renderer helper that has render_* methods which simply return
the value they are fed rather than converting them to response objects;
useful for testing purposes and special case view configuration
registrations that want to use the view configuration machinery but do
not want actual rendering to happen ."""
def __init__(self, name=None, package=None, registry=None):
# we override the initializer to avoid calling get_current_registry
# (it will return a reference to the global registry when this
# thing is called at module scope; we don't want that).
self.name = None
self.package = None
self.type = ''
self.registry = None
@property
def settings(self):
return get_current_registry().settings or {}
def render_view(self, request, value, view, context):
return value
def render(self, value, system_values, request=None):
return value
def render_to_response(self, value, system_values, request=None):
return value
def clone(self, name=None, package=None, registry=None):
return self
null_renderer = NullRendererHelper()
pyramid-1.4.5/pyramid/scripting.py 0000664 0001750 0001750 00000012535 12203712502 016503 0 ustar takaki takaki from pyramid.config import global_registries
from pyramid.exceptions import ConfigurationError
from pyramid.request import Request
from pyramid.interfaces import (
IRequestExtensions,
IRequestFactory,
IRootFactory,
)
from pyramid.threadlocal import manager as threadlocal_manager
from pyramid.traversal import DefaultRootFactory
def get_root(app, request=None):
""" Return a tuple composed of ``(root, closer)`` when provided a
:term:`router` instance as the ``app`` argument. The ``root``
returned is the application root object. The ``closer`` returned
is a callable (accepting no arguments) that should be called when
your scripting application is finished using the root.
``request`` is passed to the :app:`Pyramid` application root
factory to compute the root. If ``request`` is None, a default
will be constructed using the registry's :term:`Request Factory`
via the :meth:`pyramid.interfaces.IRequestFactory.blank` method.
"""
registry = app.registry
if request is None:
request = _make_request('/', registry)
threadlocals = {'registry':registry, 'request':request}
app.threadlocal_manager.push(threadlocals)
def closer(request=request): # keep request alive via this function default
app.threadlocal_manager.pop()
root = app.root_factory(request)
return root, closer
def prepare(request=None, registry=None):
""" This function pushes data onto the Pyramid threadlocal stack
(request and registry), making those objects 'current'. It
returns a dictionary useful for bootstrapping a Pyramid
application in a scripting environment.
``request`` is passed to the :app:`Pyramid` application root
factory to compute the root. If ``request`` is None, a default
will be constructed using the registry's :term:`Request Factory`
via the :meth:`pyramid.interfaces.IRequestFactory.blank` method.
If ``registry`` is not supplied, the last registry loaded from
:attr:`pyramid.config.global_registries` will be used. If you
have loaded more than one :app:`Pyramid` application in the
current process, you may not want to use the last registry
loaded, thus you can search the ``global_registries`` and supply
the appropriate one based on your own criteria.
The function returns a dictionary composed of ``root``,
``closer``, ``registry``, ``request`` and ``root_factory``. The
``root`` returned is the application's root resource object. The
``closer`` returned is a callable (accepting no arguments) that
should be called when your scripting application is finished
using the root. ``registry`` is the registry object passed or
the last registry loaded into
:attr:`pyramid.config.global_registries` if no registry is passed.
``request`` is the request object passed or the constructed request
if no request is passed. ``root_factory`` is the root factory used
to construct the root.
"""
if registry is None:
registry = getattr(request, 'registry', global_registries.last)
if registry is None:
raise ConfigurationError('No valid Pyramid applications could be '
'found, make sure one has been created '
'before trying to activate it.')
if request is None:
request = _make_request('/', registry)
# NB: even though _make_request might have already set registry on
# request, we reset it in case someone has passed in their own
# request.
request.registry = registry
threadlocals = {'registry':registry, 'request':request}
threadlocal_manager.push(threadlocals)
extensions = registry.queryUtility(IRequestExtensions)
if extensions is not None:
request._set_extensions(extensions)
def closer():
threadlocal_manager.pop()
root_factory = registry.queryUtility(IRootFactory,
default=DefaultRootFactory)
root = root_factory(request)
if getattr(request, 'context', None) is None:
request.context = root
return {'root':root, 'closer':closer, 'registry':registry,
'request':request, 'root_factory':root_factory}
def _make_request(path, registry=None):
""" Return a :meth:`pyramid.request.Request` object anchored at a
given path. The object returned will be generated from the supplied
registry's :term:`Request Factory` using the
:meth:`pyramid.interfaces.IRequestFactory.blank` method.
This request object can be passed to :meth:`pyramid.scripting.get_root`
or :meth:`pyramid.scripting.prepare` to initialize an application in
preparation for executing a script with a proper environment setup.
URLs can then be generated with the object, as well as rendering
templates.
If ``registry`` is not supplied, the last registry loaded from
:attr:`pyramid.config.global_registries` will be used. If you have
loaded more than one :app:`Pyramid` application in the current
process, you may not want to use the last registry loaded, thus
you can search the ``global_registries`` and supply the appropriate
one based on your own criteria.
"""
if registry is None:
registry = global_registries.last
request_factory = registry.queryUtility(IRequestFactory, default=Request)
request = request_factory.blank(path)
request.registry = registry
return request
pyramid-1.4.5/pyramid/traversal.py 0000664 0001750 0001750 00000105504 12210154301 016476 0 ustar takaki takaki import warnings
from zope.deprecation import deprecated
from zope.interface import implementer
from zope.interface.interfaces import IInterface
from repoze.lru import lru_cache
from pyramid.interfaces import (
IResourceURL,
IRequestFactory,
ITraverser,
VH_ROOT_KEY,
)
with warnings.catch_warnings():
warnings.filterwarnings('ignore')
from pyramid.interfaces import IContextURL
from pyramid.compat import (
PY3,
native_,
text_,
ascii_native_,
text_type,
binary_type,
is_nonstr_iter,
decode_path_info,
unquote_bytes_to_wsgi,
)
from pyramid.encode import url_quote
from pyramid.exceptions import URLDecodeError
from pyramid.location import lineage
from pyramid.threadlocal import get_current_registry
empty = text_('')
def find_root(resource):
""" Find the root node in the resource tree 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(resource):
if location.__parent__ is None:
resource = location
break
return resource
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.resource_path` or
:func:`pyramid.traversal.resource_path_tuple`), return a resource 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 resource tree does not exist), a
:exc:`KeyError` will be raised.
This function is the logical inverse of
: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
first character in the path string is the ``/``
character, the path is considered absolute and the resource tree
traversal will start at the root resource. If the first character
of the path string is *not* the ``/`` character, the path is
considered relative and resource tree 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.resource_path` function generates strings
which follow these rules (albeit only absolute ones).
Rules for passing *text* (Unicode) as the ``path`` argument are the same
as those for a string. In particular, the text may not have any nonascii
characters in it.
Rules for passing a *tuple* as the ``path`` argument: if the first
element in the path tuple is the empty string (for example ``('',
'a', 'b', 'c')``, the path is considered absolute and the resource tree
traversal will start at the resource tree 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 resource tree
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 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`, although doing so
will emit a deprecation warning.
"""
if isinstance(path, text_type):
path = ascii_native_(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
find_model = find_resource # b/w compat (forever)
def find_interface(resource, class_or_interface):
"""
Return the first resource found in the :term:`lineage` 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 resource providing ``interface_or_class``
can be found in the lineage. The ``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(resource):
if test(location):
return location
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_resource`: it can be used to generate
path references that can later be resolved via that function.
The ``resource`` passed in *must* be :term:`location`-aware.
.. note::
Each segment in the path string returned will use the ``__name__``
attribute of the resource it represents within the resource tree. 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 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.resource_path` function will
attempt to append it to a string and it will cause a
:exc:`pyramid.exceptions.URLDecodeError`.
.. 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 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``, although doing so will cause
a deprecation warning to be emitted.
"""
# joining strings is a bit expensive so we delegate to a function
# which caches the joined result for us
return _join_path_tuple(resource_path_tuple(resource, *elements))
model_path = resource_path # b/w compat (forever)
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.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:`resource` object) found
via traversal or url dispatch. If the ``path`` passed in is the
empty string, the value of the ``resource`` argument passed to this
function is returned.
- ``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
``resource`` argument passed to this function is returned.
- ``view_name``: The :term:`view name` found during
: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
the empty string. The ``view_name`` will be the empty string if
there is no element which follows the ``context`` path. An
example: if the path passed is ``/foo/bar``, and a resource
object is found at ``/foo`` (but not at ``/foo/bar``), the 'view
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 ``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
subpath will be the empty sequence. An example: if the path
passed is ``/foo/bar/baz/buz``, and a resource 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 ``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.
- ``traversed``: The sequence of path elements traversed from the
root to find the ``context`` object during :term:`traversal`.
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 ``resource`` is a resource found
via :term:`url dispatch`, traversed will be None.
- ``virtual_root``: A resource object representing the 'virtual' root
of the resource tree 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 resource object (the object at which :term:`traversal`
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
resource passed in).
- ``virtual_root_path`` -- If :term:`traversal` was used to find
the ``resource``, this will be the sequence of path elements
traversed to find the ``virtual_root`` resource. Each of these
items is a Unicode object. If no path segments were traversed
to find the ``virtual_root`` resource (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 ``resource``, this will be
``None``.
If the path cannot be resolved, a :exc:`KeyError` will be raised.
Rules for passing a *string* as the ``path`` argument: if the
first character in the path string is the with the ``/``
character, the path will considered absolute and the resource tree
traversal will start at the root resource. If the first character
of the path string is *not* the ``/`` character, the path is
considered relative and resource tree 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.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
element in the path tuple is the empty string (for example ``('',
'a', 'b', 'c')``, the path is considered absolute and the resource tree
traversal will start at the resource tree 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 resource tree
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 resource name).
Explanation of the conversion of ``path`` segment values to
Unicode during traversal: Each segment is URL-unquoted, and
decoded into Unicode. Each segment is assumed to be encoded using
the UTF-8 encoding (or a subset, such as ASCII); a
:exc:`pyramid.exceptions.URLDecodeError` is raised if a segment
cannot be decoded. If a segment name is empty or if it is ``.``,
it is ignored. If a segment name is ``..``, the previous segment
is deleted, and the ``..`` is ignored. As a result of this
process, the return values ``view_name``, each element in the
``subpath``, each element in ``traversed``, and each element in
the ``virtual_root_path`` will be Unicode as opposed to a string,
and will be URL-decoded.
"""
if is_nonstr_iter(path):
# the traverser factory expects PATH_INFO to be a string, not
# unicode and it expects path segments to be utf-8 and
# urlencoded (it's the same traverser which accepts PATH_INFO
# from user agents; user agents always send strings).
if path:
path = _join_path_tuple(tuple(path))
else:
path = ''
# The user is supposed to pass us a string object, never Unicode. In
# practice, however, users indeed pass Unicode to this API. If they do
# pass a Unicode object, its data *must* be entirely encodeable to ASCII,
# so we encode it here as a convenience to the user and to prevent
# second-order failures from cropping up (all failures will occur at this
# step rather than later down the line as the result of calling
# ``traversal_path``).
path = ascii_native_(path)
if path and path[0] == '/':
resource = find_root(resource)
reg = get_current_registry()
request_factory = reg.queryUtility(IRequestFactory)
if request_factory is None:
from pyramid.request import Request # avoid circdep
request_factory = Request
request = request_factory.blank(path)
request.registry = reg
traverser = reg.queryAdapter(resource, ITraverser)
if traverser is None:
traverser = ResourceTreeTraverser(resource)
return traverser(request)
def resource_path_tuple(resource, *elements):
"""
Return a tuple representing the absolute physical path of the
``resource`` object based on its position in a resource tree, e.g
``('', 'foo', 'bar')``. Any positional arguments passed in as
``elements`` will be appended as elements in the tuple
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
leading empty string element in a path tuple represents that the
path is absolute).
This function is a logical inverse of
:func:`pyramid.traversal.find_resource`: it can be used to
generate path references that can later be resolved by that function.
The ``resource`` passed in *must* be :term:`location`-aware.
.. note::
Each segment in the path tuple returned will equal the ``__name__``
attribute of the resource it represents within the resource tree. 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 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` 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 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``, although doing so will cause a deprecation
warning to be emitted.
"""
return tuple(_resource_path_list(resource, *elements))
model_path_tuple = resource_path_tuple # b/w compat (forever)
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
_model_path_list = _resource_path_list # b/w compat, not an API
def virtual_root(resource, request):
"""
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 resource at the traversal path ``/cms`` at
``http://example.com/`` instead of rooting it at
``http://example.com/cms/``.
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_resource`
API will be used to find the virtual root resource using this path;
if the resource is found, it will be returned. If the
``HTTP_X_VHM_ROOT`` key is not present in the WSGI environment,
the physical :term:`root` of the resource tree will be returned instead.
Virtual roots are not useful at all in applications that use
: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 ``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((resource, request), IContextURL)
if urlgenerator is None:
urlgenerator = TraversalContextURL(resource, request)
return urlgenerator.virtual_root()
def traversal_path(path):
""" Variant of :func:`pyramid.traversal.traversal_path_info` suitable for
decoding paths that are URL-encoded.
If this function is passed a Unicode object instead of a sequence of
bytes as ``path``, that Unicode object *must* directly encodeable to
ASCII. For example, u'/foo' will work but u'/' (a
Unicode object with characters that cannot be encoded to ascii) will
not. A :exc:`UnicodeEncodeError` will be raised if the Unicode cannot be
encoded directly to ASCII.
"""
if isinstance(path, text_type):
# must not possess characters outside ascii
path = path.encode('ascii')
# we unquote this path exactly like a PEP 3333 server would
path = unquote_bytes_to_wsgi(path) # result will be a native string
return traversal_path_info(path) # result will be a tuple of unicode
@lru_cache(1000)
def traversal_path_info(path):
""" Given``path``, return a tuple representing that path which can be
used to traverse a resource tree. ``path`` is assumed to be an
already-URL-decoded ``str`` type as if it had come to us from an upstream
WSGI server as the ``PATH_INFO`` environ variable.
The ``path`` is first decoded to from its WSGI representation to Unicode;
it is decoded differently depending on platform:
- On Python 2, ``path`` is decoded to Unicode from bytes using the UTF-8
decoding directly; a :exc:`pyramid.exc.URLDecodeError` is raised if a the
URL cannot be decoded.
- On Python 3, as per the PEP 3333 spec, ``path`` is first encoded to
bytes using the Latin-1 encoding; the resulting set of bytes is
subsequently decoded to text using the UTF-8 encoding; a
:exc:`pyramid.exc.URLDecodeError` is raised if a the URL cannot be
decoded.
The ``path`` is split on slashes, creating a list of segments. If a
segment name is empty or if it is ``.``, it is ignored. If a segment
name is ``..``, the previous segment is deleted, and the ``..`` is
ignored.
Examples:
``/``
()
``/foo/bar/baz``
(u'foo', u'bar', u'baz')
``foo/bar/baz``
(u'foo', u'bar', u'baz')
``/foo/bar/baz/``
(u'foo', u'bar', u'baz')
``/foo//bar//baz/``
(u'foo', u'bar', u'baz')
``/foo/bar/baz/..``
(u'foo', u'bar')
``/my%20archives/hello``
(u'my archives', u'hello')
``/archives/La%20Pe%C3%B1a``
(u'archives', u'')
.. note::
This function does not generate the same type of tuples 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.resource_path_tuple`. As a result,
tuples generated by ``traversal_path`` are not resolveable by the
: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 writing
applications in :app:`Pyramid`.
"""
try:
path = decode_path_info(path) # result will be Unicode
except UnicodeDecodeError as e:
raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
return split_path_info(path) # result will be tuple of Unicode
@lru_cache(1000)
def split_path_info(path):
# suitable for splitting an already-unquoted-already-decoded (unicode)
# path value
path = path.strip('/')
clean = []
for segment in path.split('/'):
if not segment or segment == '.':
continue
elif segment == '..':
if clean:
del clean[-1]
else:
clean.append(segment)
return tuple(clean)
_segment_cache = {}
quote_path_segment_doc = """ \
Return a quoted representation of a 'path segment' (such as
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
URL-quoted using Python's :mod:`urllib.quote`. If the segment
passed in is not a string or unicode object, an error will be
raised. The return value of ``quote_path_segment`` is always a
string, never Unicode.
You may pass a string of characters that need not be encoded as
the ``safe`` argument to this function. This corresponds to the
``safe`` argument to :mod:`urllib.quote`.
.. note::
The return value for each segment passed to this
function is cached in a module-scope dictionary for
speed: the cached version is returned when possible
rather than recomputing the quoted version. No cache
emptying is ever done for the lifetime of an
application, however. If you pass arbitrary
user-supplied strings to this function (as opposed to
some bounded set of values from a 'working set' known to
your application), it may become a memory leak.
"""
if PY3: # pragma: no cover
# special-case on Python 2 for speed? unchecked
def quote_path_segment(segment, safe=''):
""" %s """ % quote_path_segment_doc
# The bit of this code that deals with ``_segment_cache`` is an
# optimization: we cache all the computation of URL path segments
# in this module-scope dictionary with the original string (or
# unicode value) as the key, so we can look it up later without
# needing to reencode or re-url-quote it
try:
return _segment_cache[(segment, safe)]
except KeyError:
if segment.__class__ not in (text_type, binary_type):
segment = str(segment)
result = url_quote(native_(segment, 'utf-8'), safe)
# we don't need a lock to mutate _segment_cache, as the below
# will generate exactly one Python bytecode (STORE_SUBSCR)
_segment_cache[(segment, safe)] = result
return result
else:
def quote_path_segment(segment, safe=''):
""" %s """ % quote_path_segment_doc
# The bit of this code that deals with ``_segment_cache`` is an
# optimization: we cache all the computation of URL path segments
# in this module-scope dictionary with the original string (or
# unicode value) as the key, so we can look it up later without
# needing to reencode or re-url-quote it
try:
return _segment_cache[(segment, safe)]
except KeyError:
if segment.__class__ is text_type: #isinstance slighly slower (~15%)
result = url_quote(segment.encode('utf-8'), safe)
else:
result = url_quote(str(segment), safe)
# we don't need a lock to mutate _segment_cache, as the below
# will generate exactly one Python bytecode (STORE_SUBSCR)
_segment_cache[(segment, safe)] = result
return result
slash = text_('/')
@implementer(ITraverser)
class ResourceTreeTraverser(object):
""" A resource tree traverser that should be used (for speed) when
every resource in the tree supplies a ``__name__`` and
``__parent__`` attribute (ie. every resource in the tree is
:term:`location` aware) ."""
VIEW_SELECTOR = '@@'
def __init__(self, root):
self.root = root
def __call__(self, request):
environ = request.environ
matchdict = request.matchdict
if matchdict is not None:
path = matchdict.get('traverse', slash) or slash
if is_nonstr_iter(path):
# this is a *traverse stararg (not a {traverse})
# routing has already decoded these elements, so we just
# need to join them
path = '/' + slash.join(path) or slash
subpath = matchdict.get('subpath', ())
if not is_nonstr_iter(subpath):
# this is not a *subpath stararg (just a {subpath})
# routing has already decoded this string, so we just need
# to split it
subpath = split_path_info(subpath)
else:
# this request did not match a route
subpath = ()
try:
# empty if mounted under a path in mod_wsgi, for example
path = request.path_info or slash
except KeyError:
# if environ['PATH_INFO'] is just not there
path = slash
except UnicodeDecodeError as e:
raise URLDecodeError(e.encoding, e.object, e.start, e.end,
e.reason)
if VH_ROOT_KEY in environ:
# HTTP_X_VHM_ROOT
vroot_path = decode_path_info(environ[VH_ROOT_KEY])
vroot_tuple = split_path_info(vroot_path)
vpath = vroot_path + path # both will (must) be unicode or asciistr
vroot_idx = len(vroot_tuple) -1
else:
vroot_tuple = ()
vpath = path
vroot_idx = -1
root = self.root
ob = vroot = root
if vpath == slash: # invariant: vpath must not be empty
# prevent a call to traversal_path if we know it's going
# to return the empty tuple
vpath_tuple = ()
else:
# we do dead reckoning here via tuple slicing instead of
# pushing and popping temporary lists for speed purposes
# and this hurts readability; apologies
i = 0
view_selector = self.VIEW_SELECTOR
vpath_tuple = split_path_info(vpath)
for segment in vpath_tuple:
if segment[:2] == view_selector:
return {'context':ob,
'view_name':segment[2:],
'subpath':vpath_tuple[i+1:],
'traversed':vpath_tuple[:vroot_idx+i+1],
'virtual_root':vroot,
'virtual_root_path':vroot_tuple,
'root':root}
try:
getitem = ob.__getitem__
except AttributeError:
return {'context':ob,
'view_name':segment,
'subpath':vpath_tuple[i+1:],
'traversed':vpath_tuple[:vroot_idx+i+1],
'virtual_root':vroot,
'virtual_root_path':vroot_tuple,
'root':root}
try:
next = getitem(segment)
except KeyError:
return {'context':ob,
'view_name':segment,
'subpath':vpath_tuple[i+1:],
'traversed':vpath_tuple[:vroot_idx+i+1],
'virtual_root':vroot,
'virtual_root_path':vroot_tuple,
'root':root}
if i == vroot_idx:
vroot = next
ob = next
i += 1
return {'context':ob, 'view_name':empty, 'subpath':subpath,
'traversed':vpath_tuple, 'virtual_root':vroot,
'virtual_root_path':vroot_tuple, 'root':root}
ModelGraphTraverser = ResourceTreeTraverser # b/w compat, not API, used in wild
@implementer(IResourceURL, IContextURL)
class ResourceURL(object):
vroot_varname = VH_ROOT_KEY
def __init__(self, resource, request):
physical_path = resource_path(resource)
if physical_path != '/':
physical_path = physical_path + '/'
virtual_path = physical_path
environ = request.environ
vroot_path = environ.get(self.vroot_varname)
# if the physical path starts with the virtual root path, trim it out
# of the virtual path
if vroot_path is not None:
if physical_path.startswith(vroot_path):
virtual_path = physical_path[len(vroot_path):]
self.virtual_path = virtual_path # IResourceURL attr
self.physical_path = physical_path # IResourceURL attr
# bw compat for IContextURL methods
self.resource = resource
self.context = resource
self.request = request
# IContextURL method (deprecated in 1.3)
def virtual_root(self):
environ = self.request.environ
vroot_varname = self.vroot_varname
if vroot_varname in environ:
return find_resource(self.context, environ[vroot_varname])
# shortcut instead of using find_root; we probably already
# have it on the request
try:
return self.request.root
except AttributeError:
return find_root(self.context)
# IContextURL method (deprecated in 1.3)
def __call__(self):
""" Generate a URL based on the :term:`lineage` of a :term:`resource`
object that is ``self.context``. 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.
"""
local_url = getattr(self.context, '__resource_url__', None)
if local_url is not None:
result = local_url(
self.request,
{'virtual_path':self.virtual_path,
'physical_path':self.physical_path},
)
if result is not None:
# allow it to punt by returning ``None``
return result
app_url = self.request.application_url # never ends in a slash
return app_url + self.virtual_path
TraversalContextURL = ResourceURL # deprecated as of 1.3
deprecated(
'TraversalContextURL',
'As of Pyramid 1.3 the, "pyramid.traversal.TraversalContextURL" class is '
'scheduled to be removed. Use the '
'"pyramid.config.Configurator.add_resource_url_adapter" method to register '
'a class that implements "pyramid.interfaces.IResourceURL" instead. '
'See the "What\'s new In Pyramid 1.3" document for a further description.'
)
@lru_cache(1000)
def _join_path_tuple(tuple):
return tuple and '/'.join([quote_path_segment(x) for x in tuple]) or '/'
class DefaultRootFactory:
__parent__ = None
__name__ = None
def __init__(self, request):
matchdict = request.matchdict
# provide backwards compatibility for applications which
# used routes (at least apps without any custom "context
# factory") in BFG 0.9.X and before
if matchdict is not None:
self.__dict__.update(matchdict)
pyramid-1.4.5/pyramid/authentication.py 0000664 0001750 0001750 00000121357 12210154276 017531 0 ustar takaki takaki import binascii
from codecs import utf_8_decode
from codecs import utf_8_encode
import hashlib
import base64
import datetime
import re
import time as time_mod
import warnings
from zope.interface import implementer
from pyramid.compat import (
long,
text_type,
binary_type,
url_unquote,
url_quote,
bytes_,
ascii_native_,
)
from pyramid.interfaces import (
IAuthenticationPolicy,
IDebugLogger,
)
from pyramid.security import (
Authenticated,
Everyone,
)
from pyramid.util import strings_differ
VALID_TOKEN = re.compile(r"^[A-Za-z][A-Za-z0-9+_-]*$")
class CallbackAuthenticationPolicy(object):
""" Abstract class """
debug = False
callback = None
def _log(self, msg, methodname, request):
logger = request.registry.queryUtility(IDebugLogger)
if logger:
cls = self.__class__
classname = cls.__module__ + '.' + cls.__name__
methodname = classname + '.' + methodname
logger.debug(methodname + ': ' + msg)
def _clean_principal(self, princid):
if princid in (Authenticated, Everyone):
princid = None
return princid
def authenticated_userid(self, request):
""" Return the authenticated userid or ``None``.
If no callback is registered, this will be the same as
``unauthenticated_userid``.
If a ``callback`` is registered, this will return the userid if
and only if the callback returns a value that is not ``None``.
"""
debug = self.debug
userid = self.unauthenticated_userid(request)
if userid is None:
debug and self._log(
'call to unauthenticated_userid returned None; returning None',
'authenticated_userid',
request)
return None
if self._clean_principal(userid) is None:
debug and self._log(
('use of userid %r is disallowed by any built-in Pyramid '
'security policy, returning None' % userid),
'authenticated_userid' ,
request)
return None
if self.callback is None:
debug and self._log(
'there was no groupfinder callback; returning %r' % (userid,),
'authenticated_userid',
request)
return userid
callback_ok = self.callback(userid, request)
if callback_ok is not None: # is not None!
debug and self._log(
'groupfinder callback returned %r; returning %r' % (
callback_ok, userid),
'authenticated_userid',
request
)
return userid
debug and self._log(
'groupfinder callback returned None; returning None',
'authenticated_userid',
request
)
def effective_principals(self, request):
""" A list of effective principals derived from request.
This will return a list of principals including, at least,
:data:`pyramid.security.Everyone`. If there is no authenticated
userid, or the ``callback`` returns ``None``, this will be the
only principal:
.. code-block:: python
return [Everyone]
If the ``callback`` does not return ``None`` and an authenticated
userid is found, then the principals will include
:data:`pyramid.security.Authenticated`, the ``authenticated_userid``
and the list of principals returned by the ``callback``:
.. code-block:: python
extra_principals = callback(userid, request)
return [Everyone, Authenticated, userid] + extra_principals
"""
debug = self.debug
effective_principals = [Everyone]
userid = self.unauthenticated_userid(request)
if userid is None:
debug and self._log(
'unauthenticated_userid returned %r; returning %r' % (
userid, effective_principals),
'effective_principals',
request
)
return effective_principals
if self._clean_principal(userid) is None:
debug and self._log(
('unauthenticated_userid returned disallowed %r; returning %r '
'as if it was None' % (userid, effective_principals)),
'effective_principals',
request
)
return effective_principals
if self.callback is None:
debug and self._log(
'groupfinder callback is None, so groups is []',
'effective_principals',
request)
groups = []
else:
groups = self.callback(userid, request)
debug and self._log(
'groupfinder callback returned %r as groups' % (groups,),
'effective_principals',
request)
if groups is None: # is None!
debug and self._log(
'returning effective principals: %r' % (
effective_principals,),
'effective_principals',
request
)
return effective_principals
effective_principals.append(Authenticated)
effective_principals.append(userid)
effective_principals.extend(groups)
debug and self._log(
'returning effective principals: %r' % (
effective_principals,),
'effective_principals',
request
)
return effective_principals
@implementer(IAuthenticationPolicy)
class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
obtains data from the :mod:`repoze.who` 1.X WSGI 'API' (the
``repoze.who.identity`` key in the WSGI environment).
Constructor Arguments
``identifier_name``
Default: ``auth_tkt``. The :mod:`repoze.who` plugin name that
performs remember/forget. Optional.
``callback``
Default: ``None``. A callback passed the :mod:`repoze.who` identity
and the :term:`request`, expected to return ``None`` if the user
represented by the identity doesn't exist or a sequence of principal
identifiers (possibly empty) representing groups if the user does
exist. If ``callback`` is None, the userid will be assumed to exist
with no group principals.
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
def __init__(self, identifier_name='auth_tkt', callback=None):
self.identifier_name = identifier_name
self.callback = callback
def _get_identity(self, request):
return request.environ.get('repoze.who.identity')
def _get_identifier(self, request):
plugins = request.environ.get('repoze.who.plugins')
if plugins is None:
return None
identifier = plugins[self.identifier_name]
return identifier
def authenticated_userid(self, request):
""" Return the authenticated userid or ``None``.
If no callback is registered, this will be the same as
``unauthenticated_userid``.
If a ``callback`` is registered, this will return the userid if
and only if the callback returns a value that is not ``None``.
"""
identity = self._get_identity(request)
if identity is None:
self.debug and self._log(
'repoze.who identity is None, returning None',
'authenticated_userid',
request)
return None
userid = identity['repoze.who.userid']
if userid is None:
self.debug and self._log(
'repoze.who.userid is None, returning None' % userid,
'authenticated_userid',
request)
return None
if self._clean_principal(userid) is None:
self.debug and self._log(
('use of userid %r is disallowed by any built-in Pyramid '
'security policy, returning None' % userid),
'authenticated_userid',
request)
return None
if self.callback is None:
return userid
if self.callback(identity, request) is not None: # is not None!
return userid
def unauthenticated_userid(self, request):
""" Return the ``repoze.who.userid`` key from the detected identity."""
identity = self._get_identity(request)
if identity is None:
return None
return identity['repoze.who.userid']
def effective_principals(self, request):
""" A list of effective principals derived from the identity.
This will return a list of principals including, at least,
:data:`pyramid.security.Everyone`. If there is no identity, or
the ``callback`` returns ``None``, this will be the only principal.
If the ``callback`` does not return ``None`` and an identity is
found, then the principals will include
:data:`pyramid.security.Authenticated`, the ``authenticated_userid``
and the list of principals returned by the ``callback``.
"""
effective_principals = [Everyone]
identity = self._get_identity(request)
if identity is None:
self.debug and self._log(
('repoze.who identity was None; returning %r' %
effective_principals),
'effective_principals',
request
)
return effective_principals
if self.callback is None:
groups = []
else:
groups = self.callback(identity, request)
if groups is None: # is None!
self.debug and self._log(
('security policy groups callback returned None; returning %r' %
effective_principals),
'effective_principals',
request
)
return effective_principals
userid = identity['repoze.who.userid']
if userid is None:
self.debug and self._log(
('repoze.who.userid was None; returning %r' %
effective_principals),
'effective_principals',
request
)
return effective_principals
if self._clean_principal(userid) is None:
self.debug and self._log(
('unauthenticated_userid returned disallowed %r; returning %r '
'as if it was None' % (userid, effective_principals)),
'effective_principals',
request
)
return effective_principals
effective_principals.append(Authenticated)
effective_principals.append(userid)
effective_principals.extend(groups)
return effective_principals
def remember(self, request, principal, **kw):
""" Store the ``principal`` as ``repoze.who.userid``."""
identifier = self._get_identifier(request)
if identifier is None:
return []
environ = request.environ
identity = {'repoze.who.userid':principal}
return identifier.remember(environ, identity)
def forget(self, request):
""" Forget the current authenticated user.
Return headers that, if included in a response, will delete the
cookie responsible for tracking the current user.
"""
identifier = self._get_identifier(request)
if identifier is None:
return []
identity = self._get_identity(request)
return identifier.forget(request.environ, identity)
@implementer(IAuthenticationPolicy)
class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
obtains data from the ``REMOTE_USER`` WSGI environment variable.
Constructor Arguments
``environ_key``
Default: ``REMOTE_USER``. The key in the WSGI environ which
provides the userid.
``callback``
Default: ``None``. A callback passed the userid and the request,
expected to return None if the userid doesn't exist or a sequence of
principal identifiers (possibly empty) representing groups if the
user does exist. If ``callback`` is None, the userid will be assumed
to exist with no group principals.
``debug``
Default: ``False``. If ``debug`` is ``True``, log messages to the
Pyramid debug logger about the results of various authentication
steps. The output from debugging is useful for reporting to maillist
or IRC channels when asking for support.
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
def __init__(self, environ_key='REMOTE_USER', callback=None, debug=False):
self.environ_key = environ_key
self.callback = callback
self.debug = debug
def unauthenticated_userid(self, request):
""" The ``REMOTE_USER`` value found within the ``environ``."""
return request.environ.get(self.environ_key)
def remember(self, request, principal, **kw):
""" A no-op. The ``REMOTE_USER`` does not provide a protocol for
remembering the user. This will be application-specific and can
be done somewhere else or in a subclass."""
return []
def forget(self, request):
""" A no-op. The ``REMOTE_USER`` does not provide a protocol for
forgetting the user. This will be application-specific and can
be done somewhere else or in a subclass."""
return []
_marker = object()
@implementer(IAuthenticationPolicy)
class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
"""A :app:`Pyramid` :term:`authentication policy` which
obtains data from a Pyramid "auth ticket" cookie.
.. warning::
The default hash algorithm used in this policy is MD5 and has known
hash collision vulnerabilities. The risk of an exploit is low.
However, for improved authentication security, use
``hashalg='sha512'``.
Constructor Arguments
``secret``
The secret (a string) used for auth_tkt cookie signing.
Required.
``callback``
Default: ``None``. A callback passed the userid and the
request, expected to return ``None`` if the userid doesn't
exist or a sequence of principal identifiers (possibly empty) if
the user does exist. If ``callback`` is ``None``, the userid
will be assumed to exist with no principals. Optional.
``cookie_name``
Default: ``auth_tkt``. The cookie name used
(string). Optional.
``secure``
Default: ``False``. Only send the cookie back over a secure
conn. Optional.
``include_ip``
Default: ``False``. Make the requesting IP address part of
the authentication data in the cookie. Optional.
``timeout``
Default: ``None``. Maximum number of seconds which a newly
issued ticket will be considered valid. After this amount of
time, the ticket will expire (effectively logging the user
out). If this value is ``None``, the ticket never expires.
Optional.
``reissue_time``
Default: ``None``. If this parameter is set, it represents the number
of seconds that must pass before an authentication token cookie is
automatically reissued as the result of a request which requires
authentication. The duration is measured as the number of seconds
since the last auth_tkt cookie was issued and 'now'. If this value is
``0``, a new ticket cookie will be reissued on every request which
requires authentication.
A good rule of thumb: if you want auto-expired cookies based on
inactivity: set the ``timeout`` value to 1200 (20 mins) and set the
``reissue_time`` value to perhaps a tenth of the ``timeout`` value
(120 or 2 mins). It's nonsensical to set the ``timeout`` value lower
than the ``reissue_time`` value, as the ticket will never be reissued
if so. However, such a configuration is not explicitly prevented.
Optional.
``max_age``
Default: ``None``. The max age of the auth_tkt cookie, in
seconds. This differs from ``timeout`` inasmuch as ``timeout``
represents the lifetime of the ticket contained in the cookie,
while this value represents the lifetime of the cookie itself.
When this value is set, the cookie's ``Max-Age`` and
``Expires`` settings will be set, allowing the auth_tkt cookie
to last between browser sessions. It is typically nonsensical
to set this to a value that is lower than ``timeout`` or
``reissue_time``, although it is not explicitly prevented.
Optional.
``path``
Default: ``/``. The path for which the auth_tkt cookie is valid.
May be desirable if the application only serves part of a domain.
Optional.
``http_only``
Default: ``False``. Hide cookie from JavaScript by setting the
HttpOnly flag. Not honored by all browsers.
Optional.
``wild_domain``
Default: ``True``. An auth_tkt cookie will be generated for the
wildcard domain.
Optional.
``hashalg``
Default: ``md5`` (the literal string).
Any hash algorithm supported by Python's ``hashlib.new()`` function
can be used as the ``hashalg``.
Cookies generated by different instances of AuthTktAuthenticationPolicy
using different ``hashalg`` options are not compatible. Switching the
``hashalg`` will imply that all existing users with a valid cookie will
be required to re-login.
A warning is emitted at startup if an explicit ``hashalg`` is not
passed. This is for backwards compatibility reasons.
This option is available as of :app:`Pyramid` 1.4.
Optional.
.. note::
``md5`` is the default for backwards compatibility reasons. However,
if you don't specify ``md5`` as the hashalg explicitly, a warning is
issued at application startup time. An explicit value of ``sha512``
is recommended for improved security, and ``sha512`` will become the
default in a future Pyramid version.
``debug``
Default: ``False``. If ``debug`` is ``True``, log messages to the
Pyramid debug logger about the results of various authentication
steps. The output from debugging is useful for reporting to maillist
or IRC channels when asking for support.
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
def __init__(self,
secret,
callback=None,
cookie_name='auth_tkt',
secure=False,
include_ip=False,
timeout=None,
reissue_time=None,
max_age=None,
path="/",
http_only=False,
wild_domain=True,
debug=False,
hashalg=_marker
):
if hashalg is _marker:
hashalg = 'md5'
warnings.warn(
'The MD5 hash function used by default by the '
'AuthTktAuthenticationPolicy is known to be '
'susceptible to collision attacks. It is the current default '
'for backwards compatibility reasons, but we recommend that '
'you use the SHA512 algorithm instead for improved security. '
'Pass ``hashalg=\'sha512\'`` to the '
'AuthTktAuthenticationPolicy constructor to do so.\n\nNote '
'that a change to the hash algorithms will invalidate existing '
'auth tkt cookies set by your application. If backwards '
'compatibility of existing auth tkt cookies is of greater '
'concern than the risk posed by the potential for a hash '
'collision, you\'ll want to continue using MD5 explicitly. '
'To do so, pass ``hashalg=\'md5\'`` in your application to '
'the AuthTktAuthenticationPolicy constructor. When you do so '
'this warning will not be emitted again. The default '
'algorithm used in this policy will change in the future, so '
'setting an explicit hashalg will futureproof your '
'application.',
DeprecationWarning,
stacklevel=2
)
self.cookie = AuthTktCookieHelper(
secret,
cookie_name=cookie_name,
secure=secure,
include_ip=include_ip,
timeout=timeout,
reissue_time=reissue_time,
max_age=max_age,
http_only=http_only,
path=path,
wild_domain=wild_domain,
hashalg=hashalg,
)
self.callback = callback
self.debug = debug
def unauthenticated_userid(self, request):
""" The userid key within the auth_tkt cookie."""
result = self.cookie.identify(request)
if result:
return result['userid']
def remember(self, request, principal, **kw):
""" Accepts the following kw args: ``max_age=,
``tokens=``.
Return a list of headers which will set appropriate cookies on
the response.
"""
return self.cookie.remember(request, principal, **kw)
def forget(self, request):
""" A list of headers which will delete appropriate cookies."""
return self.cookie.forget(request)
def b64encode(v):
return base64.b64encode(bytes_(v)).strip().replace(b'\n', b'')
def b64decode(v):
return base64.b64decode(bytes_(v))
# this class licensed under the MIT license (stolen from Paste)
class AuthTicket(object):
"""
This class represents an authentication token. You must pass in
the shared secret, the userid, and the IP address. Optionally you
can include tokens (a list of strings, representing role names),
'user_data', which is arbitrary data available for your own use in
later scripts. Lastly, you can override the cookie name and
timestamp.
Once you provide all the arguments, use .cookie_value() to
generate the appropriate authentication ticket.
Usage::
token = AuthTicket('sharedsecret', 'username',
os.environ['REMOTE_ADDR'], tokens=['admin'])
val = token.cookie_value()
"""
def __init__(self, secret, userid, ip, tokens=(), user_data='',
time=None, cookie_name='auth_tkt', secure=False,
hashalg='md5'):
self.secret = secret
self.userid = userid
self.ip = ip
self.tokens = ','.join(tokens)
self.user_data = user_data
if time is None:
self.time = time_mod.time()
else:
self.time = time
self.cookie_name = cookie_name
self.secure = secure
self.hashalg = hashalg
def digest(self):
return calculate_digest(
self.ip, self.time, self.secret, self.userid, self.tokens,
self.user_data, self.hashalg)
def cookie_value(self):
v = '%s%08x%s!' % (self.digest(), int(self.time),
url_quote(self.userid))
if self.tokens:
v += self.tokens + '!'
v += self.user_data
return v
# this class licensed under the MIT license (stolen from Paste)
class BadTicket(Exception):
"""
Exception raised when a ticket can't be parsed. If we get far enough to
determine what the expected digest should have been, expected is set.
This should not be shown by default, but can be useful for debugging.
"""
def __init__(self, msg, expected=None):
self.expected = expected
Exception.__init__(self, msg)
# this function licensed under the MIT license (stolen from Paste)
def parse_ticket(secret, ticket, ip, hashalg='md5'):
"""
Parse the ticket, returning (timestamp, userid, tokens, user_data).
If the ticket cannot be parsed, a ``BadTicket`` exception will be raised
with an explanation.
"""
ticket = ticket.strip('"')
digest_size = hashlib.new(hashalg).digest_size * 2
digest = ticket[:digest_size]
try:
timestamp = int(ticket[digest_size:digest_size + 8], 16)
except ValueError as e:
raise BadTicket('Timestamp is not a hex integer: %s' % e)
try:
userid, data = ticket[digest_size + 8:].split('!', 1)
except ValueError:
raise BadTicket('userid is not followed by !')
userid = url_unquote(userid)
if '!' in data:
tokens, user_data = data.split('!', 1)
else: # pragma: no cover (never generated)
# @@: Is this the right order?
tokens = ''
user_data = data
expected = calculate_digest(ip, timestamp, secret,
userid, tokens, user_data, hashalg)
# Avoid timing attacks (see
# http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf)
if strings_differ(expected, digest):
raise BadTicket('Digest signature is not correct',
expected=(expected, digest))
tokens = tokens.split(',')
return (timestamp, userid, tokens, user_data)
# this function licensed under the MIT license (stolen from Paste)
def calculate_digest(ip, timestamp, secret, userid, tokens, user_data,
hashalg='md5'):
secret = bytes_(secret, 'utf-8')
userid = bytes_(userid, 'utf-8')
tokens = bytes_(tokens, 'utf-8')
user_data = bytes_(user_data, 'utf-8')
hash_obj = hashlib.new(hashalg)
hash_obj.update(
encode_ip_timestamp(ip, timestamp) + secret + userid + b'\0'
+ tokens + b'\0' + user_data)
digest = hash_obj.hexdigest()
hash_obj2 = hashlib.new(hashalg)
hash_obj2.update(bytes_(digest) + secret)
return hash_obj2.hexdigest()
# this function licensed under the MIT license (stolen from Paste)
def encode_ip_timestamp(ip, timestamp):
ip_chars = ''.join(map(chr, map(int, ip.split('.'))))
t = int(timestamp)
ts = ((t & 0xff000000) >> 24,
(t & 0xff0000) >> 16,
(t & 0xff00) >> 8,
t & 0xff)
ts_chars = ''.join(map(chr, ts))
return bytes_(ip_chars + ts_chars)
EXPIRE = object()
class AuthTktCookieHelper(object):
"""
A helper class for use in third-party authentication policy
implementations. See
:class:`pyramid.authentication.AuthTktAuthenticationPolicy` for the
meanings of the constructor arguments.
"""
parse_ticket = staticmethod(parse_ticket) # for tests
AuthTicket = AuthTicket # for tests
BadTicket = BadTicket # for tests
now = None # for tests
userid_type_decoders = {
'int':int,
'unicode':lambda x: utf_8_decode(x)[0], # bw compat for old cookies
'b64unicode': lambda x: utf_8_decode(b64decode(x))[0],
'b64str': lambda x: b64decode(x),
}
userid_type_encoders = {
int: ('int', str),
long: ('int', str),
text_type: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
binary_type: ('b64str', lambda x: b64encode(x)),
}
def __init__(self, secret, cookie_name='auth_tkt', secure=False,
include_ip=False, timeout=None, reissue_time=None,
max_age=None, http_only=False, path="/", wild_domain=True,
hashalg='md5'):
self.secret = secret
self.cookie_name = cookie_name
self.include_ip = include_ip
self.secure = secure
self.timeout = timeout
self.reissue_time = reissue_time
self.max_age = max_age
self.http_only = http_only
self.path = path
self.wild_domain = wild_domain
self.hashalg = hashalg
static_flags = []
if self.secure:
static_flags.append('; Secure')
if self.http_only:
static_flags.append('; HttpOnly')
self.static_flags = "".join(static_flags)
def _get_cookies(self, environ, value, max_age=None):
if max_age is EXPIRE:
max_age = "; Max-Age=0; Expires=Wed, 31-Dec-97 23:59:59 GMT"
elif max_age is not None:
later = datetime.datetime.utcnow() + datetime.timedelta(
seconds=int(max_age))
# Wdy, DD-Mon-YY HH:MM:SS GMT
expires = later.strftime('%a, %d %b %Y %H:%M:%S GMT')
# the Expires header is *required* at least for IE7 (IE7 does
# not respect Max-Age)
max_age = "; Max-Age=%s; Expires=%s" % (max_age, expires)
else:
max_age = ''
cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME'))
# While Chrome, IE, and Firefox can cope, Opera (at least) cannot
# cope with a port number in the cookie domain when the URL it
# receives the cookie from does not also have that port number in it
# (e.g via a proxy). In the meantime, HTTP_HOST is sent with port
# number, and neither Firefox nor Chrome do anything with the
# information when it's provided in a cookie domain except strip it
# out. So we strip out any port number from the cookie domain
# aggressively to avoid problems. See also
# https://github.com/Pylons/pyramid/issues/131
if ':' in cur_domain:
cur_domain = cur_domain.split(':', 1)[0]
cookies = [
('Set-Cookie', '%s="%s"; Path=%s%s%s' % (
self.cookie_name, value, self.path, max_age, self.static_flags)),
('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % (
self.cookie_name, value, self.path, cur_domain, max_age,
self.static_flags)),
]
if self.wild_domain:
wild_domain = '.' + cur_domain
cookies.append(('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % (
self.cookie_name, value, self.path, wild_domain, max_age,
self.static_flags)))
return cookies
def identify(self, request):
""" Return a dictionary with authentication information, or ``None``
if no valid auth_tkt is attached to ``request``"""
environ = request.environ
cookie = request.cookies.get(self.cookie_name)
if cookie is None:
return None
if self.include_ip:
remote_addr = environ['REMOTE_ADDR']
else:
remote_addr = '0.0.0.0'
try:
timestamp, userid, tokens, user_data = self.parse_ticket(
self.secret, cookie, remote_addr, self.hashalg)
except self.BadTicket:
return None
now = self.now # service tests
if now is None:
now = time_mod.time()
if self.timeout and ( (timestamp + self.timeout) < now ):
# the auth_tkt data has expired
return None
userid_typename = 'userid_type:'
user_data_info = user_data.split('|')
for datum in filter(None, user_data_info):
if datum.startswith(userid_typename):
userid_type = datum[len(userid_typename):]
decoder = self.userid_type_decoders.get(userid_type)
if decoder:
userid = decoder(userid)
reissue = self.reissue_time is not None
if reissue and not hasattr(request, '_authtkt_reissued'):
if ( (now - timestamp) > self.reissue_time ):
# work around https://github.com/Pylons/pyramid/issues#issue/108
tokens = list(filter(None, tokens))
headers = self.remember(request, userid, max_age=self.max_age,
tokens=tokens)
def reissue_authtkt(request, response):
if not hasattr(request, '_authtkt_reissue_revoked'):
for k, v in headers:
response.headerlist.append((k, v))
request.add_response_callback(reissue_authtkt)
request._authtkt_reissued = True
environ['REMOTE_USER_TOKENS'] = tokens
environ['REMOTE_USER_DATA'] = user_data
environ['AUTH_TYPE'] = 'cookie'
identity = {}
identity['timestamp'] = timestamp
identity['userid'] = userid
identity['tokens'] = tokens
identity['userdata'] = user_data
return identity
def forget(self, request):
""" Return a set of expires Set-Cookie headers, which will destroy
any existing auth_tkt cookie when attached to a response"""
environ = request.environ
request._authtkt_reissue_revoked = True
return self._get_cookies(environ, '', max_age=EXPIRE)
def remember(self, request, userid, max_age=None, tokens=()):
""" Return a set of Set-Cookie headers; when set into a response,
these headers will represent a valid authentication ticket.
``max_age``
The max age of the auth_tkt cookie, in seconds. When this value is
set, the cookie's ``Max-Age`` and ``Expires`` settings will be set,
allowing the auth_tkt cookie to last between browser sessions. If
this value is ``None``, the ``max_age`` value provided to the
helper itself will be used as the ``max_age`` value. Default:
``None``.
``tokens``
A sequence of strings that will be placed into the auth_tkt tokens
field. Each string in the sequence must be of the Python ``str``
type and must match the regex ``^[A-Za-z][A-Za-z0-9+_-]*$``.
Tokens are available in the returned identity when an auth_tkt is
found in the request and unpacked. Default: ``()``.
"""
if max_age is None:
max_age = self.max_age
environ = request.environ
if self.include_ip:
remote_addr = environ['REMOTE_ADDR']
else:
remote_addr = '0.0.0.0'
user_data = ''
encoding_data = self.userid_type_encoders.get(type(userid))
if encoding_data:
encoding, encoder = encoding_data
userid = encoder(userid)
user_data = 'userid_type:%s' % encoding
new_tokens = []
for token in tokens:
if isinstance(token, text_type):
try:
token = ascii_native_(token)
except UnicodeEncodeError:
raise ValueError("Invalid token %r" % (token,))
if not (isinstance(token, str) and VALID_TOKEN.match(token)):
raise ValueError("Invalid token %r" % (token,))
new_tokens.append(token)
tokens = tuple(new_tokens)
if hasattr(request, '_authtkt_reissued'):
request._authtkt_reissue_revoked = True
ticket = self.AuthTicket(
self.secret,
userid,
remote_addr,
tokens=tokens,
user_data=user_data,
cookie_name=self.cookie_name,
secure=self.secure,
hashalg=self.hashalg
)
cookie_value = ticket.cookie_value()
return self._get_cookies(environ, cookie_value, max_age)
@implementer(IAuthenticationPolicy)
class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` authentication policy which gets its data from the
configured :term:`session`. For this authentication policy to work, you
will have to follow the instructions in the :ref:`sessions_chapter` to
configure a :term:`session factory`.
Constructor Arguments
``prefix``
A prefix used when storing the authentication parameters in the
session. Defaults to 'auth.'. Optional.
``callback``
Default: ``None``. A callback passed the userid and the
request, expected to return ``None`` if the userid doesn't
exist or a sequence of principal identifiers (possibly empty) if
the user does exist. If ``callback`` is ``None``, the userid
will be assumed to exist with no principals. Optional.
``debug``
Default: ``False``. If ``debug`` is ``True``, log messages to the
Pyramid debug logger about the results of various authentication
steps. The output from debugging is useful for reporting to maillist
or IRC channels when asking for support.
"""
def __init__(self, prefix='auth.', callback=None, debug=False):
self.callback = callback
self.prefix = prefix or ''
self.userid_key = prefix + 'userid'
self.debug = debug
def remember(self, request, principal, **kw):
""" Store a principal in the session."""
request.session[self.userid_key] = principal
return []
def forget(self, request):
""" Remove the stored principal from the session."""
if self.userid_key in request.session:
del request.session[self.userid_key]
return []
def unauthenticated_userid(self, request):
return request.session.get(self.userid_key)
@implementer(IAuthenticationPolicy)
class BasicAuthAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` authentication policy which uses HTTP standard basic
authentication protocol to authenticate users. To use this policy you will
need to provide a callback which checks the supplied user credentials
against your source of login data.
Constructor Arguments
``check``
A callback function passed a username, password and request, in that
order as positional arguments. Expected to return ``None`` if the
userid doesn't exist or a sequence of principal identifiers (possibly
empty) if the user does exist.
``realm``
Default: ``"Realm"``. The Basic Auth Realm string. Usually displayed to
the user by the browser in the login dialog.
``debug``
Default: ``False``. If ``debug`` is ``True``, log messages to the
Pyramid debug logger about the results of various authentication
steps. The output from debugging is useful for reporting to maillist
or IRC channels when asking for support.
**Issuing a challenge**
Regular browsers will not send username/password credentials unless they
first receive a challenge from the server. The following recipe will
register a view that will send a Basic Auth challenge to the user whenever
there is an attempt to call a view which results in a Forbidden response::
from pyramid.httpexceptions import HTTPForbidden
from pyramid.httpexceptions import HTTPUnauthorized
from pyramid.security import forget
from pyramid.view import view_config
@view_config(context=HTTPForbidden)
def basic_challenge(request):
response = HTTPUnauthorized()
response.headers.update(forget(request))
return response
"""
def __init__(self, check, realm='Realm', debug=False):
self.check = check
self.realm = realm
self.debug = debug
def unauthenticated_userid(self, request):
""" The userid parsed from the ``Authorization`` request header."""
credentials = self._get_credentials(request)
if credentials:
return credentials[0]
def remember(self, request, principal, **kw):
""" A no-op. Basic authentication does not provide a protocol for
remembering the user. Credentials are sent on every request.
"""
return []
def forget(self, request):
""" Returns challenge headers. This should be attached to a response
to indicate that credentials are required."""
return [('WWW-Authenticate', 'Basic realm="%s"' % self.realm)]
def callback(self, username, request):
# Username arg is ignored. Unfortunately _get_credentials winds up
# getting called twice when authenticated_userid is called. Avoiding
# that, however, winds up duplicating logic from the superclass.
credentials = self._get_credentials(request)
if credentials:
username, password = credentials
return self.check(username, password, request)
def _get_credentials(self, request):
authorization = request.headers.get('Authorization')
if not authorization:
return None
try:
authmeth, auth = authorization.split(' ', 1)
except ValueError: # not enough values to unpack
return None
if authmeth.lower() != 'basic':
return None
try:
auth = b64decode(auth.strip()).decode('ascii')
except (TypeError, binascii.Error): # can't decode
return None
try:
username, password = auth.split(':', 1)
except ValueError: # not enough values to unpack
return None
return username, password
pyramid-1.4.5/pyramid/path.py 0000664 0001750 0001750 00000036622 12210154276 015446 0 ustar takaki takaki import os
import pkg_resources
import sys
import imp
from zope.interface import implementer
from pyramid.interfaces import IAssetDescriptor
from pyramid.compat import string_types
ignore_types = [ imp.C_EXTENSION, imp.C_BUILTIN ]
init_names = [ '__init__%s' % x[0] for x in imp.get_suffixes() if
x[0] and x[2] not in ignore_types ]
def caller_path(path, level=2):
if not os.path.isabs(path):
module = caller_module(level+1)
prefix = package_path(module)
path = os.path.join(prefix, path)
return path
def caller_module(level=2, sys=sys):
module_globals = sys._getframe(level).f_globals
module_name = module_globals.get('__name__') or '__main__'
module = sys.modules[module_name]
return module
def package_name(pkg_or_module):
""" If this function is passed a module, return the dotted Python
package name of the package in which the module lives. If this
function is passed a package, return the dotted Python package
name of the package itself."""
if pkg_or_module is None or pkg_or_module.__name__ == '__main__':
return '__main__'
pkg_filename = pkg_or_module.__file__
pkg_name = pkg_or_module.__name__
splitted = os.path.split(pkg_filename)
if splitted[-1] in init_names:
# it's a package
return pkg_name
return pkg_name.rsplit('.', 1)[0]
def package_of(pkg_or_module):
""" Return the package of a module or return the package itself """
pkg_name = package_name(pkg_or_module)
__import__(pkg_name)
return sys.modules[pkg_name]
def caller_package(level=2, caller_module=caller_module):
# caller_module in arglist for tests
module = caller_module(level+1)
f = getattr(module, '__file__', '')
if (('__init__.py' in f) or ('__init__$py' in f)): # empty at >>>
# Module is a package
return module
# Go up one level to get package
package_name = module.__name__.rsplit('.', 1)[0]
return sys.modules[package_name]
def package_path(package):
# computing the abspath is actually kinda expensive so we memoize
# the result
prefix = getattr(package, '__abspath__', None)
if prefix is None:
prefix = pkg_resources.resource_filename(package.__name__, '')
# pkg_resources doesn't care whether we feed it a package
# name or a module name within the package, the result
# will be the same: a directory name to the package itself
try:
package.__abspath__ = prefix
except:
# this is only an optimization, ignore any error
pass
return prefix
class _CALLER_PACKAGE(object):
def __repr__(self): # pragma: no cover (for docs)
return 'pyramid.path.CALLER_PACKAGE'
CALLER_PACKAGE = _CALLER_PACKAGE()
class Resolver(object):
def __init__(self, package=CALLER_PACKAGE):
if package in (None, CALLER_PACKAGE):
self.package = package
else:
if isinstance(package, string_types):
try:
__import__(package)
except ImportError:
raise ValueError(
'The dotted name %r cannot be imported' % (package,)
)
package = sys.modules[package]
self.package = package_of(package)
def get_package_name(self):
if self.package is CALLER_PACKAGE:
package_name = caller_package().__name__
else:
package_name = self.package.__name__
return package_name
def get_package(self):
if self.package is CALLER_PACKAGE:
package = caller_package()
else:
package = self.package
return package
class AssetResolver(Resolver):
""" A class used to resolve an :term:`asset specification` to an
:term:`asset descriptor`.
.. note:: This API is new as of Pyramid 1.3.
The constructor accepts a single argument named ``package`` which may be
any of:
- A fully qualified (not relative) dotted name to a module or package
- a Python module or package object
- The value ``None``
- The constant value :attr:`pyramid.path.CALLER_PACKAGE`.
The default value is :attr:`pyramid.path.CALLER_PACKAGE`.
The ``package`` is used when a relative asset specification is supplied
to the :meth:`~pyramid.path.AssetResolver.resolve` method. An asset
specification without a colon in it is treated as relative.
If the value ``None`` is supplied as the ``package``, the resolver will
only be able to resolve fully qualified (not relative) asset
specifications. Any attempt to resolve a relative asset specification
when the ``package`` is ``None`` will result in an :exc:`ValueError`
exception.
If the value :attr:`pyramid.path.CALLER_PACKAGE` is supplied as the
``package``, the resolver will treat relative asset specifications as
relative to the caller of the :meth:`~pyramid.path.AssetResolver.resolve`
method.
If a *module* or *module name* (as opposed to a package or package name)
is supplied as ``package``, its containing package is computed and this
package used to derive the package name (all names are resolved relative
to packages, never to modules). For example, if the ``package`` argument
to this type was passed the string ``xml.dom.expatbuilder``, and
``template.pt`` is supplied to the
:meth:`~pyramid.path.AssetResolver.resolve` method, the resulting absolute
asset spec would be ``xml.minidom:template.pt``, because
``xml.dom.expatbuilder`` is a module object, not a package object.
If a *package* or *package name* (as opposed to a module or module name)
is supplied as ``package``, this package will be used to compute relative
asset specifications. For example, if the ``package`` argument to this
type was passed the string ``xml.dom``, and ``template.pt`` is supplied
to the :meth:`~pyramid.path.AssetResolver.resolve` method, the resulting
absolute asset spec would be ``xml.minidom:template.pt``.
"""
def resolve(self, spec):
"""
Resolve the asset spec named as ``spec`` to an object that has the
attributes and methods described in
:class:`pyramid.interfaces.IAssetDescriptor`.
If ``spec`` is an absolute filename
(e.g. ``/path/to/myproject/templates/foo.pt``) or an absolute asset
spec (e.g. ``myproject:templates.foo.pt``), an asset descriptor is
returned without taking into account the ``package`` passed to this
class' constructor.
If ``spec`` is a *relative* asset specification (an asset
specification without a ``:`` in it, e.g. ``templates/foo.pt``), the
``package`` argument of the constructor is used as the package
portion of the asset spec. For example:
.. code-block:: python
a = AssetResolver('myproject')
resolver = a.resolve('templates/foo.pt')
print resolver.abspath()
# -> /path/to/myproject/templates/foo.pt
If the AssetResolver is constructed without a ``package`` argument of
``None``, and a relative asset specification is passed to
``resolve``, an :exc:`ValueError` exception is raised.
"""
if os.path.isabs(spec):
return FSAssetDescriptor(spec)
path = spec
if ':' in path:
package_name, path = spec.split(':', 1)
else:
if self.package is CALLER_PACKAGE:
package_name = caller_package().__name__
else:
package_name = getattr(self.package, '__name__', None)
if package_name is None:
raise ValueError(
'relative spec %r irresolveable without package' % (spec,)
)
return PkgResourcesAssetDescriptor(package_name, path)
class DottedNameResolver(Resolver):
""" A class used to resolve a :term:`dotted Python name` to a package or
module object.
.. note:: This API is new as of Pyramid 1.3.
The constructor accepts a single argument named ``package`` which may be
any of:
- A fully qualified (not relative) dotted name to a module or package
- a Python module or package object
- The value ``None``
- The constant value :attr:`pyramid.path.CALLER_PACKAGE`.
The default value is :attr:`pyramid.path.CALLER_PACKAGE`.
The ``package`` is used when a relative dotted name is supplied to the
:meth:`~pyramid.path.DottedNameResolver.resolve` method. A dotted name
which has a ``.`` (dot) or ``:`` (colon) as its first character is
treated as relative.
If the value ``None`` is supplied as the ``package``, the resolver will
only be able to resolve fully qualified (not relative) names. Any
attempt to resolve a relative name when the ``package`` is ``None`` will
result in an :exc:`ValueError` exception.
If the value :attr:`pyramid.path.CALLER_PACKAGE` is supplied as the
``package``, the resolver will treat relative dotted names as relative to
the caller of the :meth:`~pyramid.path.DottedNameResolver.resolve`
method.
If a *module* or *module name* (as opposed to a package or package name)
is supplied as ``package``, its containing package is computed and this
package used to derive the package name (all names are resolved relative
to packages, never to modules). For example, if the ``package`` argument
to this type was passed the string ``xml.dom.expatbuilder``, and
``.mindom`` is supplied to the
:meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
import would be for ``xml.minidom``, because ``xml.dom.expatbuilder`` is
a module object, not a package object.
If a *package* or *package name* (as opposed to a module or module name)
is supplied as ``package``, this package will be used to relative compute
dotted names. For example, if the ``package`` argument to this type was
passed the string ``xml.dom``, and ``.minidom`` is supplied to the
:meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
import would be for ``xml.minidom``.
"""
def resolve(self, dotted):
"""
This method resolves a dotted name reference to a global Python
object (an object which can be imported) to the object itself.
Two dotted name styles are supported:
- ``pkg_resources``-style dotted names where non-module attributes
of a package are separated from the rest of the path using a ``:``
e.g. ``package.module:attr``.
- ``zope.dottedname``-style dotted names where non-module
attributes of a package are separated from the rest of the path
using a ``.`` e.g. ``package.module.attr``.
These styles can be used interchangeably. If the supplied name
contains a ``:`` (colon), the ``pkg_resources`` resolution
mechanism will be chosen, otherwise the ``zope.dottedname``
resolution mechanism will be chosen.
If the ``dotted`` argument passed to this method is not a string, a
:exc:`ValueError` will be raised.
When a dotted name cannot be resolved, a :exc:`ValueError` error is
raised.
Example:
.. code-block:: python
r = DottedNameResolver()
v = r.resolve('xml') # v is the xml module
"""
if not isinstance(dotted, string_types):
raise ValueError('%r is not a string' % (dotted,))
package = self.package
if package is CALLER_PACKAGE:
package = caller_package()
return self._resolve(dotted, package)
def maybe_resolve(self, dotted):
"""
This method behaves just like
:meth:`~pyramid.path.DottedNameResolver.resolve`, except if the
``dotted`` value passed is not a string, it is simply returned. For
example:
.. code-block:: python
import xml
r = DottedNameResolver()
v = r.maybe_resolve(xml)
# v is the xml module; no exception raised
"""
if isinstance(dotted, string_types):
package = self.package
if package is CALLER_PACKAGE:
package = caller_package()
return self._resolve(dotted, package)
return dotted
def _resolve(self, dotted, package):
if ':' in dotted:
return self._pkg_resources_style(dotted, package)
else:
return self._zope_dottedname_style(dotted, package)
def _pkg_resources_style(self, value, package):
""" package.module:attr style """
if value.startswith('.') or value.startswith(':'):
if not package:
raise ValueError(
'relative name %r irresolveable without package' % (value,)
)
if value in ['.', ':']:
value = package.__name__
else:
value = package.__name__ + value
return pkg_resources.EntryPoint.parse(
'x=%s' % value).load(False)
def _zope_dottedname_style(self, value, package):
""" package.module.attr style """
module = getattr(package, '__name__', None) # package may be None
if not module:
module = None
if value == '.':
if module is None:
raise ValueError(
'relative name %r irresolveable without package' % (value,)
)
name = module.split('.')
else:
name = value.split('.')
if not name[0]:
if module is None:
raise ValueError(
'relative name %r irresolveable without '
'package' % (value,)
)
module = module.split('.')
name.pop(0)
while not name[0]:
module.pop()
name.pop(0)
name = module + name
used = name.pop(0)
found = __import__(used)
for n in name:
used += '.' + n
try:
found = getattr(found, n)
except AttributeError:
__import__(used)
found = getattr(found, n) # pragma: no cover
return found
@implementer(IAssetDescriptor)
class PkgResourcesAssetDescriptor(object):
pkg_resources = pkg_resources
def __init__(self, pkg_name, path):
self.pkg_name = pkg_name
self.path = path
def absspec(self):
return '%s:%s' % (self.pkg_name, self.path)
def abspath(self):
return self.pkg_resources.resource_filename(self.pkg_name, self.path)
def stream(self):
return self.pkg_resources.resource_stream(self.pkg_name, self.path)
def isdir(self):
return self.pkg_resources.resource_isdir(self.pkg_name, self.path)
def listdir(self):
return self.pkg_resources.resource_listdir(self.pkg_name, self.path)
def exists(self):
return self.pkg_resources.resource_exists(self.pkg_name, self.path)
@implementer(IAssetDescriptor)
class FSAssetDescriptor(object):
def __init__(self, path):
self.path = os.path.abspath(path)
def absspec(self):
raise NotImplementedError
def abspath(self):
return self.path
def stream(self):
return open(self.path, 'rb')
def isdir(self):
return os.path.isdir(self.path)
def listdir(self):
return os.listdir(self.path)
def exists(self):
return os.path.exists(self.path)
pyramid-1.4.5/pyramid/chameleon_text.py 0000664 0001750 0001750 00000002343 12210154276 017502 0 ustar takaki takaki from zope.interface import implementer
from chameleon.zpt.template import PageTextTemplateFile
from pyramid.interfaces import ITemplateRenderer
from pyramid.decorator import reify
from pyramid import renderers
def renderer_factory(info):
return renderers.template_renderer_factory(info, TextTemplateRenderer)
@implementer(ITemplateRenderer)
class TextTemplateRenderer(object):
def __init__(self, path, lookup, macro=None):
self.path = path
self.lookup = lookup
# text template renderers have no macros, so we ignore the
# macro arg
@reify # avoid looking up reload_templates before manager pushed
def template(self):
return PageTextTemplateFile(self.path,
auto_reload=self.lookup.auto_reload,
debug=self.lookup.debug,
translate=self.lookup.translate)
def implementation(self):
return self.template
def __call__(self, value, system):
try:
system.update(value)
except (TypeError, ValueError):
raise ValueError('renderer was passed non-dictionary as value')
result = self.template(**system)
return result
pyramid-1.4.5/pyramid/compat.py 0000664 0001750 0001750 00000015222 12203712502 015760 0 ustar takaki takaki import inspect
import platform
import sys
import types
if platform.system() == 'Windows': # pragma: no cover
WIN = True
else: # pragma: no cover
WIN = False
try: # pragma: no cover
import __pypy__
PYPY = True
except: # pragma: no cover
__pypy__ = None
PYPY = False
try:
import cPickle as pickle
except ImportError: # pragma: no cover
import pickle
# True if we are running on Python 3.
PY3 = sys.version_info[0] == 3
if PY3: # pragma: no cover
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
long = int
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
long = long
def text_(s, encoding='latin-1', errors='strict'):
""" If ``s`` is an instance of ``binary_type``, return
``s.decode(encoding, errors)``, otherwise return ``s``"""
if isinstance(s, binary_type):
return s.decode(encoding, errors)
return s # pragma: no cover
def bytes_(s, encoding='latin-1', errors='strict'):
""" If ``s`` is an instance of ``text_type``, return
``s.encode(encoding, errors)``, otherwise return ``s``"""
if isinstance(s, text_type): # pragma: no cover
return s.encode(encoding, errors)
return s
if PY3: # pragma: no cover
def ascii_native_(s):
if isinstance(s, text_type):
s = s.encode('ascii')
return str(s, 'ascii', 'strict')
else:
def ascii_native_(s):
if isinstance(s, text_type):
s = s.encode('ascii')
return str(s)
ascii_native_.__doc__ = """
Python 3: If ``s`` is an instance of ``text_type``, return
``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')``
Python 2: If ``s`` is an instance of ``text_type``, return
``s.encode('ascii')``, otherwise return ``str(s)``
"""
if PY3: # pragma: no cover
def native_(s, encoding='latin-1', errors='strict'):
""" If ``s`` is an instance of ``text_type``, return
``s``, otherwise return ``str(s, encoding, errors)``"""
if isinstance(s, text_type):
return s
return str(s, encoding, errors)
else:
def native_(s, encoding='latin-1', errors='strict'):
""" If ``s`` is an instance of ``text_type``, return
``s.encode(encoding, errors)``, otherwise return ``str(s)``"""
if isinstance(s, text_type):
return s.encode(encoding, errors)
return str(s)
native_.__doc__ = """
Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
return ``str(s, encoding, errors)``
Python 2: If ``s`` is an instance of ``text_type``, return
``s.encode(encoding, errors)``, otherwise return ``str(s)``
"""
if PY3: # pragma: no cover
from urllib import parse
urlparse = parse
from urllib.parse import quote as url_quote
from urllib.parse import quote_plus as url_quote_plus
from urllib.parse import unquote as url_unquote
from urllib.parse import urlencode as url_encode
from urllib.request import urlopen as url_open
url_unquote_text = url_unquote
url_unquote_native = url_unquote
else:
import urlparse
from urllib import quote as url_quote
from urllib import quote_plus as url_quote_plus
from urllib import unquote as url_unquote
from urllib import urlencode as url_encode
from urllib2 import urlopen as url_open
def url_unquote_text(v, encoding='utf-8', errors='replace'): # pragma: no cover
v = url_unquote(v)
return v.decode(encoding, errors)
def url_unquote_native(v, encoding='utf-8', errors='replace'): # pragma: no cover
return native_(url_unquote_text(v, encoding, errors))
if PY3: # pragma: no cover
import builtins
exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None):
if value is None:
value = tp
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
del builtins
else: # pragma: no cover
def exec_(code, globs=None, locs=None):
"""Execute code in a namespace."""
if globs is None:
frame = sys._getframe(1)
globs = frame.f_globals
if locs is None:
locs = frame.f_locals
del frame
elif locs is None:
locs = globs
exec("""exec code in globs, locs""")
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
if PY3: # pragma: no cover
def iteritems_(d):
return d.items()
def itervalues_(d):
return d.values()
def iterkeys_(d):
return d.keys()
else:
def iteritems_(d):
return d.iteritems()
def itervalues_(d):
return d.itervalues()
def iterkeys_(d):
return d.iterkeys()
if PY3: # pragma: no cover
def map_(*arg):
return list(map(*arg))
else:
map_ = map
if PY3: # pragma: no cover
def is_nonstr_iter(v):
if isinstance(v, str):
return False
return hasattr(v, '__iter__')
else:
def is_nonstr_iter(v):
return hasattr(v, '__iter__')
if PY3: # pragma: no cover
im_func = '__func__'
im_self = '__self__'
else:
im_func = 'im_func'
im_self = 'im_self'
try: # pragma: no cover
import configparser
except ImportError: # pragma: no cover
import ConfigParser as configparser
try:
from Cookie import SimpleCookie
except ImportError: # pragma: no cover
from http.cookies import SimpleCookie
if PY3: # pragma: no cover
from html import escape
else:
from cgi import escape
try: # pragma: no cover
input_ = raw_input
except NameError: # pragma: no cover
input_ = input
try:
from StringIO import StringIO as NativeIO
except ImportError: # pragma: no cover
from io import StringIO as NativeIO
# "json" is not an API; it's here to support older pyramid_debugtoolbar
# versions which attempt to import it
import json
if PY3: # pragma: no cover
# see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before
# decoding it to utf-8
def decode_path_info(path):
return path.encode('latin-1').decode('utf-8')
else:
def decode_path_info(path):
return path.decode('utf-8')
if PY3: # pragma: no cover
# see PEP 3333 for why we decode the path to latin-1
from urllib.parse import unquote_to_bytes
def unquote_bytes_to_wsgi(bytestring):
return unquote_to_bytes(bytestring).decode('latin-1')
else:
from urlparse import unquote as unquote_to_bytes
def unquote_bytes_to_wsgi(bytestring):
return unquote_to_bytes(bytestring)
def is_bound_method(ob):
return inspect.ismethod(ob) and getattr(ob, im_self, None) is not None
pyramid-1.4.5/pyramid/config/ 0000775 0001750 0001750 00000000000 12210157153 015371 5 ustar takaki takaki pyramid-1.4.5/pyramid/config/predicates.py 0000664 0001750 0001750 00000020236 12210154276 020074 0 ustar takaki takaki import re
from pyramid.exceptions import ConfigurationError
from pyramid.compat import is_nonstr_iter
from pyramid.traversal import (
find_interface,
traversal_path,
resource_path_tuple
)
from pyramid.urldispatch import _compile_route
from pyramid.util import object_description
from pyramid.session import check_csrf_token
from pyramid.security import effective_principals
from .util import as_sorted_tuple
_marker = object()
class XHRPredicate(object):
def __init__(self, val, config):
self.val = bool(val)
def text(self):
return 'xhr = %s' % self.val
phash = text
def __call__(self, context, request):
return bool(request.is_xhr) is self.val
class RequestMethodPredicate(object):
def __init__(self, val, config):
request_method = as_sorted_tuple(val)
if 'GET' in request_method and 'HEAD' not in request_method:
# GET implies HEAD too
request_method = as_sorted_tuple(request_method + ('HEAD',))
self.val = request_method
def text(self):
return 'request_method = %s' % (','.join(self.val))
phash = text
def __call__(self, context, request):
return request.method in self.val
class PathInfoPredicate(object):
def __init__(self, val, config):
self.orig = val
try:
val = re.compile(val)
except re.error as why:
raise ConfigurationError(why.args[0])
self.val = val
def text(self):
return 'path_info = %s' % (self.orig,)
phash = text
def __call__(self, context, request):
return self.val.match(request.upath_info) is not None
class RequestParamPredicate(object):
def __init__(self, val, config):
val = as_sorted_tuple(val)
reqs = []
for p in val:
k = p
v = None
if '=' in p:
k, v = p.split('=', 1)
k, v = k.strip(), v.strip()
reqs.append((k, v))
self.val = val
self.reqs = reqs
def text(self):
return 'request_param %s' % ','.join(
['%s=%s' % (x,y) if y else x for x, y in self.reqs]
)
phash = text
def __call__(self, context, request):
for k, v in self.reqs:
actual = request.params.get(k)
if actual is None:
return False
if v is not None and actual != v:
return False
return True
class HeaderPredicate(object):
def __init__(self, val, config):
name = val
v = None
if ':' in name:
name, val_str = name.split(':', 1)
try:
v = re.compile(val_str)
except re.error as why:
raise ConfigurationError(why.args[0])
if v is None:
self._text = 'header %s' % (name,)
else:
self._text = 'header %s=%s' % (name, val_str)
self.name = name
self.val = v
def text(self):
return self._text
phash = text
def __call__(self, context, request):
if self.val is None:
return self.name in request.headers
val = request.headers.get(self.name)
if val is None:
return False
return self.val.match(val) is not None
class AcceptPredicate(object):
def __init__(self, val, config):
self.val = val
def text(self):
return 'accept = %s' % (self.val,)
phash = text
def __call__(self, context, request):
return self.val in request.accept
class ContainmentPredicate(object):
def __init__(self, val, config):
self.val = config.maybe_dotted(val)
def text(self):
return 'containment = %s' % (self.val,)
phash = text
def __call__(self, context, request):
ctx = getattr(request, 'context', context)
return find_interface(ctx, self.val) is not None
class RequestTypePredicate(object):
def __init__(self, val, config):
self.val = val
def text(self):
return 'request_type = %s' % (self.val,)
phash = text
def __call__(self, context, request):
return self.val.providedBy(request)
class MatchParamPredicate(object):
def __init__(self, val, config):
val = as_sorted_tuple(val)
self.val = val
reqs = [ p.split('=', 1) for p in val ]
self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
def text(self):
return 'match_param %s' % ','.join(
['%s=%s' % (x,y) for x, y in self.reqs]
)
phash = text
def __call__(self, context, request):
if not request.matchdict:
# might be None
return False
for k, v in self.reqs:
if request.matchdict.get(k) != v:
return False
return True
class CustomPredicate(object):
def __init__(self, func, config):
self.func = func
def text(self):
return getattr(
self.func,
'__text__',
'custom predicate: %s' % object_description(self.func)
)
def phash(self):
# using hash() here rather than id() is intentional: we
# want to allow custom predicates that are part of
# frameworks to be able to define custom __hash__
# functions for custom predicates, so that the hash output
# of predicate instances which are "logically the same"
# may compare equal.
return 'custom:%r' % hash(self.func)
def __call__(self, context, request):
return self.func(context, request)
class TraversePredicate(object):
# 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
# ResourceTreeTraverser to use the resolved traverse pattern as the
# traversal path.
def __init__(self, val, config):
_, self.tgenerate = _compile_route(val)
self.val = val
def text(self):
return 'traverse matchdict pseudo-predicate'
def phash(self):
# This isn't actually a predicate, it's just a infodict modifier that
# injects ``traverse`` into the matchdict. As a result, we don't
# need to update the hash.
return ''
def __call__(self, context, request):
if 'traverse' in context:
return True
m = context['match']
tvalue = self.tgenerate(m) # tvalue will be urlquoted string
m['traverse'] = traversal_path(tvalue)
# This isn't actually a predicate, it's just a infodict modifier that
# injects ``traverse`` into the matchdict. As a result, we just
# return True.
return True
class CheckCSRFTokenPredicate(object):
check_csrf_token = staticmethod(check_csrf_token) # testing
def __init__(self, val, config):
self.val = val
def text(self):
return 'check_csrf = %s' % (self.val,)
phash = text
def __call__(self, context, request):
val = self.val
if val:
if val is True:
val = 'csrf_token'
return self.check_csrf_token(request, val, raises=False)
return True
class PhysicalPathPredicate(object):
def __init__(self, val, config):
if is_nonstr_iter(val):
self.val = tuple(val)
else:
val = tuple(filter(None, val.split('/')))
self.val = ('',) + val
def text(self):
return 'physical_path = %s' % (self.val,)
phash = text
def __call__(self, context, request):
if getattr(context, '__name__', _marker) is not _marker:
return resource_path_tuple(context) == self.val
return False
class EffectivePrincipalsPredicate(object):
def __init__(self, val, config):
if is_nonstr_iter(val):
self.val = set(val)
else:
self.val = set((val,))
def text(self):
return 'effective_principals = %s' % sorted(list(self.val))
phash = text
def __call__(self, context, request):
req_principals = effective_principals(request)
if is_nonstr_iter(req_principals):
rpset = set(req_principals)
if self.val.issubset(rpset):
return True
return False
pyramid-1.4.5/pyramid/config/adapters.py 0000664 0001750 0001750 00000033011 12210154276 017547 0 ustar takaki takaki from functools import update_wrapper
from zope.interface import Interface
from pyramid.interfaces import (
IResponse,
ITraverser,
IResourceURL,
)
from pyramid.config.util import (
action_method,
takes_one_arg,
)
class AdaptersConfiguratorMixin(object):
@action_method
def add_subscriber(self, subscriber, iface=None, **predicates):
"""Add an event :term:`subscriber` for the event stream
implied by the supplied ``iface`` interface.
The ``subscriber`` argument represents a callable object (or a
:term:`dotted Python name` which identifies a callable); it will be
called with a single object ``event`` whenever :app:`Pyramid` emits
an :term:`event` associated with the ``iface``, which may be an
:term:`interface` or a class or a :term:`dotted Python name` to a
global object representing an interface or a class.
Using the default ``iface`` value, ``None`` will cause the subscriber
to be registered for all event types. See :ref:`events_chapter` for
more information about events and subscribers.
Any number of predicate keyword arguments may be passed in
``**predicates``. Each predicate named will narrow the set of
circumstances that the subscriber will be invoked. Each named
predicate must have been registered via
:meth:`pyramid.config.Configurator.add_subscriber_predicate` before it
can be used. See :ref:`subscriber_predicates` for more information.
.. note::
THe ``**predicates`` argument is new as of Pyramid 1.4.
"""
dotted = self.maybe_dotted
subscriber, iface = dotted(subscriber), dotted(iface)
if iface is None:
iface = (Interface,)
if not isinstance(iface, (tuple, list)):
iface = (iface,)
def register():
predlist = self.get_predlist('subscriber')
order, preds, phash = predlist.make(self, **predicates)
derived_predicates = [ self._derive_predicate(p) for p in preds ]
derived_subscriber = self._derive_subscriber(
subscriber,
derived_predicates,
)
intr.update(
{'phash':phash,
'order':order,
'predicates':preds,
'derived_predicates':derived_predicates,
'derived_subscriber':derived_subscriber,
}
)
self.registry.registerHandler(derived_subscriber, iface)
intr = self.introspectable(
'subscribers',
id(subscriber),
self.object_description(subscriber),
'subscriber'
)
intr['subscriber'] = subscriber
intr['interfaces'] = iface
self.action(None, register, introspectables=(intr,))
return subscriber
def _derive_predicate(self, predicate):
derived_predicate = predicate
if eventonly(predicate):
def derived_predicate(*arg):
return predicate(arg[0])
# seems pointless to try to fix __doc__, __module__, etc as
# predicate will invariably be an instance
return derived_predicate
def _derive_subscriber(self, subscriber, predicates):
derived_subscriber = subscriber
if eventonly(subscriber):
def derived_subscriber(*arg):
return subscriber(arg[0])
if hasattr(subscriber, '__name__'):
update_wrapper(derived_subscriber, subscriber)
if not predicates:
return derived_subscriber
def subscriber_wrapper(*arg):
# We need to accept *arg and pass it along because zope subscribers
# are designed awkwardly. Notification via
# registry.adapter.subscribers will always call an associated
# subscriber with all of the objects involved in the subscription
# lookup, despite the fact that the event sender always has the
# option to attach those objects to the event object itself, and
# almost always does.
#
# The "eventonly" jazz sprinkled in this function and related
# functions allows users to define subscribers and predicates which
# accept only an event argument without needing to accept the rest
# of the adaptation arguments. Had I been smart enough early on to
# use .subscriptions to find the subscriber functions in order to
# call them manually with a single "event" argument instead of
# relying on .subscribers to both find and call them implicitly
# with all args, the eventonly hack would not have been required.
# At this point, though, using .subscriptions and manual execution
# is not possible without badly breaking backwards compatibility.
if all((predicate(*arg) for predicate in predicates)):
return derived_subscriber(*arg)
if hasattr(subscriber, '__name__'):
update_wrapper(subscriber_wrapper, subscriber)
return subscriber_wrapper
@action_method
def add_subscriber_predicate(self, name, factory, weighs_more_than=None,
weighs_less_than=None):
"""
Adds a subscriber predicate factory. The associated subscriber
predicate can later be named as a keyword argument to
:meth:`pyramid.config.Configurator.add_subscriber` in the
``**predicates`` anonyous keyword argument dictionary.
``name`` should be the name of the predicate. It must be a valid
Python identifier (it will be used as a ``**predicates`` keyword
argument to :meth:`~pyramid.config.Configurator.add_subscriber`).
``factory`` should be a :term:`predicate factory`.
See :ref:`subscriber_predicates` for more information.
.. note::
This method is new as of Pyramid 1.4.
"""
self._add_predicate(
'subscriber',
name,
factory,
weighs_more_than=weighs_more_than,
weighs_less_than=weighs_less_than
)
@action_method
def add_response_adapter(self, adapter, type_or_iface):
""" When an object of type (or interface) ``type_or_iface`` is
returned from a view callable, Pyramid will use the adapter
``adapter`` to convert it into an object which implements the
:class:`pyramid.interfaces.IResponse` interface. If ``adapter`` is
None, an object returned of type (or interface) ``type_or_iface``
will itself be used as a response object.
``adapter`` and ``type_or_interface`` may be Python objects or
strings representing dotted names to importable Python global
objects.
See :ref:`using_iresponse` for more information."""
adapter = self.maybe_dotted(adapter)
type_or_iface = self.maybe_dotted(type_or_iface)
def register():
reg = self.registry
if adapter is None:
reg.registerSelfAdapter((type_or_iface,), IResponse)
else:
reg.registerAdapter(adapter, (type_or_iface,), IResponse)
discriminator = (IResponse, type_or_iface)
intr = self.introspectable(
'response adapters',
discriminator,
self.object_description(adapter),
'response adapter')
intr['adapter'] = adapter
intr['type'] = type_or_iface
self.action(discriminator, register, introspectables=(intr,))
def _register_response_adapters(self):
# cope with WebOb response objects that aren't decorated with IResponse
from webob import Response as WebobResponse
self.registry.registerSelfAdapter((WebobResponse,), IResponse)
@action_method
def add_traverser(self, adapter, iface=None):
"""
The superdefault :term:`traversal` algorithm that :app:`Pyramid` uses
is explained in :ref:`traversal_algorithm`. Though it is rarely
necessary, this default algorithm can be swapped out selectively for
a different traversal pattern via configuration. The section
entitled :ref:`changing_the_traverser` details how to create a
traverser class.
For example, to override the superdefault traverser used by Pyramid,
you might do something like this:
.. code-block:: python
from myapp.traversal import MyCustomTraverser
config.add_traverser(MyCustomTraverser)
This would cause the Pyramid superdefault traverser to never be used;
intead all traversal would be done using your ``MyCustomTraverser``
class, no matter which object was returned by the :term:`root
factory` of this application. Note that we passed no arguments to
the ``iface`` keyword parameter. The default value of ``iface``,
``None`` represents that the registered traverser should be used when
no other more specific traverser is available for the object returned
by the root factory.
However, more than one traversal algorithm can be active at the same
time. The traverser used can depend on the result of the :term:`root
factory`. For instance, if your root factory returns more than one
type of object conditionally, you could claim that an alternate
traverser adapter should be used agsinst one particular class or
interface returned by that root factory. When the root factory
returned an object that implemented that class or interface, a custom
traverser would be used. Otherwise, the default traverser would be
used. The ``iface`` argument represents the class of the object that
the root factory might return or an :term:`interface` that the object
might implement.
To use a particular traverser only when the root factory returns a
particular class:
.. code-block:: python
config.add_traverser(MyCustomTraverser, MyRootClass)
When more than one traverser is active, the "most specific" traverser
will be used (the one that matches the class or interface of the
value returned by the root factory most closely).
Note that either ``adapter`` or ``iface`` can be a :term:`dotted
Python name` or a Python object.
See :ref:`changing_the_traverser` for more information.
"""
iface = self.maybe_dotted(iface)
adapter= self.maybe_dotted(adapter)
def register(iface=iface):
if iface is None:
iface = Interface
self.registry.registerAdapter(adapter, (iface,), ITraverser)
discriminator = ('traverser', iface)
intr = self.introspectable(
'traversers',
discriminator,
'traverser for %r' % iface,
'traverser',
)
intr['adapter'] = adapter
intr['iface'] = iface
self.action(discriminator, register, introspectables=(intr,))
@action_method
def add_resource_url_adapter(self, adapter, resource_iface=None):
"""
When you add a traverser as described in
:ref:`changing_the_traverser`, it's convenient to continue to use the
:meth:`pyramid.request.Request.resource_url` API. However, since the
way traversal is done may have been modified, the URLs that
``resource_url`` generates by default may be incorrect when resources
are returned by a custom traverser.
If you've added a traverser, you can change how
:meth:`~pyramid.request.Request.resource_url` generates a URL for a
specific type of resource by calling this method.
The ``adapter`` argument represents a class that implements the
:class:`~pyramid.interfaces.IResourceURL` interface. The class
constructor should accept two arguments in its constructor (the
resource and the request) and the resulting instance should provide
the attributes detailed in that interface (``virtual_path`` and
``physical_path``, in particular).
The ``resource_iface`` argument represents a class or interface that
the resource should possess for this url adapter to be used when
:meth:`pyramid.request.Request.resource_url` looks up a resource url
adapter. If ``resource_iface`` is not passed, or it is passed as
``None``, the url adapter will be used for every type of resource.
See :ref:`changing_resource_url` for more information.
.. note::
This API is new in Pyramid 1.3.
"""
adapter = self.maybe_dotted(adapter)
resource_iface = self.maybe_dotted(resource_iface)
def register(resource_iface=resource_iface):
if resource_iface is None:
resource_iface = Interface
self.registry.registerAdapter(
adapter,
(resource_iface, Interface),
IResourceURL,
)
discriminator = ('resource url adapter', resource_iface)
intr = self.introspectable(
'resource url adapters',
discriminator,
'resource url adapter for resource iface %r' % resource_iface,
'resource url adapter',
)
intr['adapter'] = adapter
intr['resource_iface'] = resource_iface
self.action(discriminator, register, introspectables=(intr,))
def eventonly(callee):
return takes_one_arg(callee, argname='event')
pyramid-1.4.5/pyramid/config/factories.py 0000664 0001750 0001750 00000017032 12210154276 017730 0 ustar takaki takaki from zope.interface import implementer
from pyramid.interfaces import (
IDefaultRootFactory,
IRequestFactory,
IRequestExtensions,
IRootFactory,
ISessionFactory,
)
from pyramid.traversal import DefaultRootFactory
from pyramid.util import (
action_method,
InstancePropertyMixin,
)
class FactoriesConfiguratorMixin(object):
@action_method
def set_root_factory(self, factory):
""" Add a :term:`root factory` to the current configuration
state. If the ``factory`` argument is ``None`` a default root
factory will be registered.
.. note::
Using the ``root_factory`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
factory = self.maybe_dotted(factory)
if factory is None:
factory = DefaultRootFactory
def register():
self.registry.registerUtility(factory, IRootFactory)
self.registry.registerUtility(factory, IDefaultRootFactory) # b/c
intr = self.introspectable('root factories',
None,
self.object_description(factory),
'root factory')
intr['factory'] = factory
self.action(IRootFactory, register, introspectables=(intr,))
_set_root_factory = set_root_factory # bw compat
@action_method
def set_session_factory(self, factory):
"""
Configure the application with a :term:`session factory`. If this
method is called, the ``factory`` argument must be a session
factory callable or a :term:`dotted Python name` to that factory.
.. note::
Using the ``session_factory`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
factory = self.maybe_dotted(factory)
def register():
self.registry.registerUtility(factory, ISessionFactory)
intr = self.introspectable('session factory', None,
self.object_description(factory),
'session factory')
intr['factory'] = factory
self.action(ISessionFactory, register, introspectables=(intr,))
@action_method
def set_request_factory(self, factory):
""" The object passed as ``factory`` should be an object (or a
:term:`dotted Python name` which refers to an object) which
will be used by the :app:`Pyramid` router to create all
request objects. This factory object must have the same
methods and attributes as the
:class:`pyramid.request.Request` class (particularly
``__call__``, and ``blank``).
See :meth:`pyramid.config.Configurator.add_request_method`
for a less intrusive way to extend the request objects with
custom methods and properties.
.. note::
Using the ``request_factory`` argument to the
:class:`pyramid.config.Configurator` constructor
can be used to achieve the same purpose.
"""
factory = self.maybe_dotted(factory)
def register():
self.registry.registerUtility(factory, IRequestFactory)
intr = self.introspectable('request factory', None,
self.object_description(factory),
'request factory')
intr['factory'] = factory
self.action(IRequestFactory, register, introspectables=(intr,))
@action_method
def add_request_method(self,
callable=None,
name=None,
property=False,
reify=False):
""" Add a property or method to the request object.
When adding a method to the request, ``callable`` may be any
function that receives the request object as the first
parameter. If ``name`` is ``None`` then it will be computed
from the name of the ``callable``.
When adding a property to the request, ``callable`` can either
be a callable that accepts the request as its single positional
parameter, or it can be a property descriptor. If ``name`` is
``None``, the name of the property will be computed from the
name of the ``callable``.
If the ``callable`` is a property descriptor a ``ValueError``
will be raised if ``name`` is ``None`` or ``reify`` is ``True``.
See :meth:`pyramid.request.Request.set_property` for more
details on ``property`` vs ``reify``. When ``reify`` is
``True``, the value of ``property`` is assumed to also be
``True``.
In all cases, ``callable`` may also be a
:term:`dotted Python name` which refers to either a callable or
a property descriptor.
If ``callable`` is ``None`` then the method is only used to
assist in conflict detection between different addons requesting
the same attribute on the request object.
This is the recommended method for extending the request object
and should be used in favor of providing a custom request
factory via
:meth:`pyramid.config.Configurator.set_request_factory`.
.. versionadded:: 1.4
"""
if callable is not None:
callable = self.maybe_dotted(callable)
property = property or reify
if property:
name, callable = InstancePropertyMixin._make_property(
callable, name=name, reify=reify)
elif name is None:
name = callable.__name__
def register():
exts = self.registry.queryUtility(IRequestExtensions)
if exts is None:
exts = _RequestExtensions()
self.registry.registerUtility(exts, IRequestExtensions)
plist = exts.descriptors if property else exts.methods
plist[name] = callable
if callable is None:
self.action(('request extensions', name), None)
elif property:
intr = self.introspectable('request extensions', name,
self.object_description(callable),
'request property')
intr['callable'] = callable
intr['property'] = True
intr['reify'] = reify
self.action(('request extensions', name), register,
introspectables=(intr,))
else:
intr = self.introspectable('request extensions', name,
self.object_description(callable),
'request method')
intr['callable'] = callable
intr['property'] = False
intr['reify'] = False
self.action(('request extensions', name), register,
introspectables=(intr,))
@action_method
def set_request_property(self, callable, name=None, reify=False):
""" Add a property to the request object.
.. warning::
This method has been docs-deprecated as of Pyramid 1.4.
:meth:`pyramid.config.Configurator.add_request_method` should be
used instead.
.. versionadded:: 1.3
"""
self.add_request_method(
callable, name=name, property=not reify, reify=reify)
@implementer(IRequestExtensions)
class _RequestExtensions(object):
def __init__(self):
self.descriptors = {}
self.methods = {}
pyramid-1.4.5/pyramid/config/testing.py 0000664 0001750 0001750 00000016145 12210154276 017432 0 ustar takaki takaki from zope.interface import Interface
from pyramid.interfaces import (
ITraverser,
IAuthorizationPolicy,
IAuthenticationPolicy,
IRendererFactory,
)
from pyramid.renderers import RendererHelper
from pyramid.traversal import (
decode_path_info,
split_path_info,
)
from pyramid.util import action_method
class TestingConfiguratorMixin(object):
# testing API
def testing_securitypolicy(self, userid=None, groupids=(),
permissive=True, remember_result=None,
forget_result=None):
"""Unit/integration testing helper: Registers a pair of faux
:app:`Pyramid` security policies: a :term:`authentication
policy` and a :term:`authorization policy`.
The behavior of the registered :term:`authorization policy`
depends on the ``permissive`` argument. If ``permissive`` is
true, a permissive :term:`authorization policy` is registered;
this policy allows all access. If ``permissive`` is false, a
nonpermissive :term:`authorization policy` is registered; this
policy denies all access.
``remember_result``, if provided, should be the result returned by
the ``remember`` method of the faux authentication policy. If it is
not provided (or it is provided, and is ``None``), the default value
``[]`` (the empty list) will be returned by ``remember``.
.. note::
``remember_result`` is new as of Pyramid 1.4.
``forget_result``, if provided, should be the result returned by
the ``forget`` method of the faux authentication policy. If it is
not provided (or it is provided, and is ``None``), the default value
``[]`` (the empty list) will be returned by ``forget``.
.. note::
``forget_result`` is new as of Pyramid 1.4.
The behavior of the registered :term:`authentication policy`
depends on the values provided for the ``userid`` and
``groupids`` argument. The authentication policy will return
the userid identifier implied by the ``userid`` argument and
the group ids implied by the ``groupids`` argument when the
:func:`pyramid.security.authenticated_userid` or
:func:`pyramid.security.effective_principals` APIs are
used.
This function is most useful when testing code that uses
the APIs named :func:`pyramid.security.has_permission`,
:func:`pyramid.security.authenticated_userid`,
:func:`pyramid.security.effective_principals`, and
:func:`pyramid.security.principals_allowed_by_permission`.
"""
from pyramid.testing import DummySecurityPolicy
policy = DummySecurityPolicy(
userid, groupids, permissive, remember_result, forget_result
)
self.registry.registerUtility(policy, IAuthorizationPolicy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
return policy
def testing_resources(self, resources):
"""Unit/integration testing helper: registers a dictionary of
:term:`resource` objects that can be resolved via the
:func:`pyramid.traversal.find_resource` API.
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_resource`` (and
thus to your code) when
:func:`pyramid.traversal.find_resource` is called with an
equivalent path string or tuple.
"""
class DummyTraverserFactory:
def __init__(self, context):
self.context = context
def __call__(self, request):
path = decode_path_info(request.environ['PATH_INFO'])
ob = resources[path]
traversed = split_path_info(path)
return {'context':ob, 'view_name':'','subpath':(),
'traversed':traversed, 'virtual_root':ob,
'virtual_root_path':(), 'root':ob}
self.registry.registerAdapter(DummyTraverserFactory, (Interface,),
ITraverser)
return resources
testing_models = testing_resources # b/w compat
@action_method
def testing_add_subscriber(self, event_iface=None):
"""Unit/integration testing helper: Registers a
:term:`subscriber` which listens for events of the type
``event_iface``. This method returns a list object which is
appended to by the subscriber whenever an event is captured.
When an event is dispatched that matches the value implied by
the ``event_iface`` argument, that event will be appended to
the list. You can then compare the values in the list to
expected event notifications. This method is useful when
testing code that wants to call
:meth:`pyramid.registry.Registry.notify`,
or :func:`zope.component.event.dispatch`.
The default value of ``event_iface`` (``None``) implies a
subscriber registered for *any* kind of event.
"""
event_iface = self.maybe_dotted(event_iface)
L = []
def subscriber(*event):
L.extend(event)
self.add_subscriber(subscriber, event_iface)
return L
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 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
:func:`pyramid.renderers.render_to_response` function or
any other ``render_*`` or ``get_*`` API of the
:mod:`pyramid.renderers` module.
Note that calling this method for with a ``path`` argument
representing a renderer factory type (e.g. for ``foo.pt``
usually implies the ``chameleon_zpt`` renderer factory)
clobbers any existing renderer factory registered for that
type.
.. note:: This method is also available under the alias
``testing_add_template`` (an older name for it).
"""
from pyramid.testing import DummyRendererFactory
helper = RendererHelper(name=path, registry=self.registry)
factory = self.registry.queryUtility(IRendererFactory, name=helper.type)
if not isinstance(factory, DummyRendererFactory):
factory = DummyRendererFactory(helper.type, factory)
self.registry.registerUtility(factory, IRendererFactory,
name=helper.type)
from pyramid.testing import DummyTemplateRenderer
if renderer is None:
renderer = DummyTemplateRenderer()
factory.add(path, renderer)
return renderer
testing_add_template = testing_add_renderer
pyramid-1.4.5/pyramid/config/rendering.py 0000664 0001750 0001750 00000010011 12210154276 017714 0 ustar takaki takaki import warnings
from pyramid.interfaces import (
IRendererFactory,
IRendererGlobalsFactory,
PHASE1_CONFIG,
)
from pyramid.util import action_method
from pyramid import (
renderers,
chameleon_text,
chameleon_zpt,
)
from pyramid.mako_templating import renderer_factory as mako_renderer_factory
DEFAULT_RENDERERS = (
('.txt', chameleon_text.renderer_factory),
('.pt', chameleon_zpt.renderer_factory),
('.mak', mako_renderer_factory),
('.mako', mako_renderer_factory),
('json', renderers.json_renderer_factory),
('string', renderers.string_renderer_factory),
)
class RenderingConfiguratorMixin(object):
@action_method
def add_renderer(self, name, factory):
"""
Add a :app:`Pyramid` :term:`renderer` factory to the
current configuration state.
The ``name`` argument is the renderer name. Use ``None`` to
represent the default renderer (a renderer which will be used for all
views unless they name another renderer specifically).
The ``factory`` argument is Python reference to an
implementation of a :term:`renderer` factory or a
:term:`dotted Python name` to same.
"""
factory = self.maybe_dotted(factory)
# if name is None or the empty string, we're trying to register
# a default renderer, but registerUtility is too dumb to accept None
# as a name
if not name:
name = ''
def register():
self.registry.registerUtility(factory, IRendererFactory, name=name)
intr = self.introspectable('renderer factories',
name,
self.object_description(factory),
'renderer factory')
intr['factory'] = factory
intr['name'] = name
# we need to register renderers early (in phase 1) because they are
# used during view configuration (which happens in phase 3)
self.action((IRendererFactory, name), register, order=PHASE1_CONFIG,
introspectables=(intr,))
@action_method
def set_renderer_globals_factory(self, factory, warn=True):
""" The object passed as ``factory`` should be an callable (or
a :term:`dotted Python name` which refers to an callable) that
will be used by the :app:`Pyramid` rendering machinery as a
renderers global factory (see :ref:`adding_renderer_globals`).
The ``factory`` callable must accept a single argument named
``system`` (which will be a dictionary) and it must return a
dictionary. When an application uses a renderer, the
factory's return dictionary will be merged into the ``system``
dictionary, and therefore will be made available to the code
which uses the renderer.
.. warning::
This method is deprecated as of Pyramid 1.1. Use a BeforeRender
event subscriber as documented in the :ref:`hooks_chapter` chapter
instead.
.. note::
Using the ``renderer_globals_factory`` argument
to the :class:`pyramid.config.Configurator` constructor
can be used to achieve the same purpose.
"""
if warn:
warnings.warn(
'Calling the ``set_renderer_globals`` method of a Configurator '
'is deprecated as of Pyramid 1.1. Use a BeforeRender event '
'subscriber as documented in the "Hooks" chapter of the '
'Pyramid narrative documentation instead',
DeprecationWarning,
3)
factory = self.maybe_dotted(factory)
def register():
self.registry.registerUtility(factory, IRendererGlobalsFactory)
intr = self.introspectable('renderer globals factory', None,
self.object_description(factory),
'renderer globals factory')
intr['factory'] = factory
self.action(IRendererGlobalsFactory, register)
pyramid-1.4.5/pyramid/config/i18n.py 0000664 0001750 0001750 00000012042 12203712502 016516 0 ustar takaki takaki import os
import sys
from translationstring import ChameleonTranslate
from pyramid.interfaces import (
IChameleonTranslate,
ILocaleNegotiator,
ITranslationDirectories,
)
from pyramid.exceptions import ConfigurationError
from pyramid.i18n import get_localizer
from pyramid.path import package_path
from pyramid.threadlocal import get_current_request
from pyramid.util import action_method
class I18NConfiguratorMixin(object):
@action_method
def set_locale_negotiator(self, negotiator):
"""
Set the :term:`locale negotiator` for this application. The
:term:`locale negotiator` is a callable which accepts a
:term:`request` object and which returns a :term:`locale
name`. The ``negotiator`` argument should be the locale
negotiator implementation or a :term:`dotted Python name`
which refers to such an implementation.
Later calls to this method override earlier calls; there can
be only one locale negotiator active at a time within an
application. See :ref:`activating_translation` for more
information.
.. note::
Using the ``locale_negotiator`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
def register():
self._set_locale_negotiator(negotiator)
intr = self.introspectable('locale negotiator', None,
self.object_description(negotiator),
'locale negotiator')
intr['negotiator'] = negotiator
self.action(ILocaleNegotiator, register, introspectables=(intr,))
def _set_locale_negotiator(self, negotiator):
locale_negotiator = self.maybe_dotted(negotiator)
self.registry.registerUtility(locale_negotiator, ILocaleNegotiator)
@action_method
def add_translation_dirs(self, *specs):
""" 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:`asset specification`
names naming a directory path (e.g. ``some.package:locale``)
or a combination of the two.
Example:
.. code-block:: python
config.add_translation_dirs('/usr/share/locale',
'some.package:locale')
Later calls to ``add_translation_dir`` insert directories into the
beginning of the list of translation directories created by earlier
calls. This means that the same translation found in a directory
added later in the configuration process will be found before one
added earlier in the configuration process. However, if multiple
specs are provided in a single call to ``add_translation_dirs``, the
directories will be inserted into the beginning of the directory list
in the order they're provided in the ``*specs`` list argument (items
earlier in the list trump ones later in the list).
"""
directories = []
introspectables = []
for spec in specs[::-1]: # reversed
package_name, filename = self._split_spec(spec)
if package_name is None: # absolute filename
directory = filename
else:
__import__(package_name)
package = sys.modules[package_name]
directory = os.path.join(package_path(package), filename)
if not os.path.isdir(os.path.realpath(directory)):
raise ConfigurationError('"%s" is not a directory' %
directory)
intr = self.introspectable('translation directories', directory,
spec, 'translation directory')
intr['directory'] = directory
intr['spec'] = spec
introspectables.append(intr)
directories.append(directory)
def register():
for directory in directories:
tdirs = self.registry.queryUtility(ITranslationDirectories)
if tdirs is None:
tdirs = []
self.registry.registerUtility(tdirs,
ITranslationDirectories)
tdirs.insert(0, directory)
if directories:
# We actually only need an IChameleonTranslate function
# utility to be registered zero or one times. We register the
# same function once for each added translation directory,
# which does too much work, but has the same effect.
ctranslate = ChameleonTranslate(translator)
self.registry.registerUtility(ctranslate, IChameleonTranslate)
self.action(None, register, introspectables=introspectables)
def translator(msg):
request = get_current_request()
localizer = get_localizer(request)
return localizer.translate(msg)
pyramid-1.4.5/pyramid/config/util.py 0000664 0001750 0001750 00000012477 12210154276 016736 0 ustar takaki takaki from hashlib import md5
import inspect
from pyramid.compat import (
bytes_,
is_nonstr_iter,
)
from pyramid.compat import im_func
from pyramid.exceptions import ConfigurationError
from pyramid.registry import predvalseq
from pyramid.util import (
TopologicalSorter,
action_method,
ActionInfo,
)
action_method = action_method # support bw compat imports
ActionInfo = ActionInfo # support bw compat imports
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
def as_sorted_tuple(val):
if not is_nonstr_iter(val):
val = (val,)
val = tuple(sorted(val))
return val
# under = after
# over = before
class PredicateList(object):
def __init__(self):
self.sorter = TopologicalSorter()
self.last_added = None
def add(self, name, factory, weighs_more_than=None, weighs_less_than=None):
# Predicates should be added to a predicate list in (presumed)
# computation expense order.
## if weighs_more_than is None and weighs_less_than is None:
## weighs_more_than = self.last_added or FIRST
## weighs_less_than = LAST
self.last_added = name
self.sorter.add(
name,
factory,
after=weighs_more_than,
before=weighs_less_than,
)
def make(self, config, **kw):
# Given a configurator and a list of keywords, a predicate list is
# computed. Elsewhere in the code, we evaluate predicates using a
# generator expression. All predicates associated with a view or
# route must evaluate true for the view or route to "match" during a
# request. The fastest predicate should be evaluated first, then the
# next fastest, and so on, as if one returns false, the remainder of
# the predicates won't need to be evaluated.
#
# While we compute predicates, we also compute a predicate hash (aka
# phash) that can be used by a caller to identify identical predicate
# lists.
ordered = self.sorter.sorted()
phash = md5()
weights = []
preds = []
for n, (name, predicate_factory) in enumerate(ordered):
vals = kw.pop(name, None)
if vals is None: # XXX should this be a sentinel other than None?
continue
if not isinstance(vals, predvalseq):
vals = (vals,)
for val in vals:
pred = predicate_factory(val, config)
hashes = pred.phash()
if not is_nonstr_iter(hashes):
hashes = [hashes]
for h in hashes:
phash.update(bytes_(h))
weights.append(1 << n+1)
preds.append(pred)
if kw:
raise ConfigurationError('Unknown predicate values: %r' % (kw,))
# A "order" is computed for the predicate list. An order is
# a scoring.
#
# Each predicate is associated with a weight value. The weight of a
# predicate symbolizes the relative potential "importance" of the
# predicate to all other predicates. A larger weight indicates
# greater importance.
#
# All weights for a given predicate list are bitwise ORed together
# to create a "score"; this score is then subtracted from
# MAX_ORDER and divided by an integer representing the number of
# predicates+1 to determine the order.
#
# For views, the order represents the ordering in which a "multiview"
# ( a collection of views that share the same context/request/name
# triad but differ in other ways via predicates) will attempt to call
# its set of views. Views with lower orders will be tried first.
# The intent is to a) ensure that views with more predicates are
# always evaluated before views with fewer predicates and b) to
# ensure a stable call ordering of views that share the same number
# of predicates. Views which do not have any predicates get an order
# of MAX_ORDER, meaning that they will be tried very last.
score = 0
for bit in weights:
score = score | bit
order = (MAX_ORDER - score) / (len(preds) + 1)
return order, preds, phash.hexdigest()
def takes_one_arg(callee, attr=None, argname=None):
ismethod = False
if attr is None:
attr = '__call__'
if inspect.isroutine(callee):
fn = callee
elif inspect.isclass(callee):
try:
fn = callee.__init__
except AttributeError:
return False
ismethod = hasattr(fn, '__call__')
else:
try:
fn = getattr(callee, attr)
except AttributeError:
return False
try:
argspec = inspect.getargspec(fn)
except TypeError:
return False
args = argspec[0]
if hasattr(fn, im_func) or ismethod:
# it's an instance method (or unbound method on py2)
if not args:
return False
args = args[1:]
if not args:
return False
if len(args) == 1:
return True
if argname:
defaults = argspec[3]
if defaults is None:
defaults = ()
if args[0] == argname:
if len(args) - len(defaults) == 1:
return True
return False
pyramid-1.4.5/pyramid/config/tweens.py 0000664 0001750 0001750 00000020556 12210154276 017263 0 ustar takaki takaki from zope.interface import implementer
from pyramid.interfaces import ITweens
from pyramid.compat import (
string_types,
is_nonstr_iter,
)
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import (
excview_tween_factory,
MAIN,
INGRESS,
EXCVIEW,
)
from pyramid.config.util import (
action_method,
TopologicalSorter,
)
class TweensConfiguratorMixin(object):
def add_tween(self, tween_factory, under=None, over=None):
"""
.. note:: This feature is new as of Pyramid 1.2.
Add a 'tween factory'. A :term:`tween` (a contraction of 'between')
is a bit of code that sits between the Pyramid router's main request
handling function and the upstream WSGI component that uses
:app:`Pyramid` as its 'app'. Tweens are a feature that may be used
by Pyramid framework extensions, to provide, for example,
Pyramid-specific view timing support, bookkeeping code that examines
exceptions before they are returned to the upstream WSGI application,
or a variety of other features. Tweens behave a bit like
:term:`WSGI` 'middleware' but they have the benefit of running in a
context in which they have access to the Pyramid :term:`application
registry` as well as the Pyramid rendering machinery.
.. note:: You can view the tween ordering configured into a given
Pyramid application by using the ``ptweens``
command. See :ref:`displaying_tweens`.
The ``tween_factory`` argument must be a :term:`dotted Python name`
to a global object representing the tween factory.
The ``under`` and ``over`` arguments allow the caller of
``add_tween`` to provide a hint about where in the tween chain this
tween factory should be placed when an implicit tween chain is used.
These hints are only used when an explicit tween chain is not used
(when the ``pyramid.tweens`` configuration value is not set).
Allowable values for ``under`` or ``over`` (or both) are:
- ``None`` (the default).
- A :term:`dotted Python name` to a tween factory: a string
representing the dotted name of a tween factory added in a call to
``add_tween`` in the same configuration session.
- One of the constants :attr:`pyramid.tweens.MAIN`,
:attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`.
- An iterable of any combination of the above. This allows the user
to specify fallbacks if the desired tween is not included, as well
as compatibility with multiple other tweens.
``under`` means 'closer to the main Pyramid application than',
``over`` means 'closer to the request ingress than'.
For example, calling ``add_tween('myapp.tfactory',
over=pyramid.tweens.MAIN)`` will attempt to place the tween factory
represented by the dotted name ``myapp.tfactory`` directly 'above'
(in ``ptweens`` order) the main Pyramid request handler.
Likewise, calling ``add_tween('myapp.tfactory',
over=pyramid.tweens.MAIN, under='mypkg.someothertween')`` will
attempt to place this tween factory 'above' the main handler but
'below' (a fictional) 'mypkg.someothertween' tween factory.
If all options for ``under`` (or ``over``) cannot be found in the
current configuration, it is an error. If some options are specified
purely for compatibilty with other tweens, just add a fallback of
MAIN or INGRESS. For example, ``under=('mypkg.someothertween',
'mypkg.someothertween2', INGRESS)``. This constraint will require
the tween to be located under both the 'mypkg.someothertween' tween,
the 'mypkg.someothertween2' tween, and INGRESS. If any of these is
not in the current configuration, this constraint will only organize
itself based on the tweens that are present.
Specifying neither ``over`` nor ``under`` is equivalent to specifying
``under=INGRESS``.
Implicit tween ordering is obviously only best-effort. Pyramid will
attempt to present an implicit order of tweens as best it can, but
the only surefire way to get any particular ordering is to use an
explicit tween order. A user may always override the implicit tween
ordering by using an explicit ``pyramid.tweens`` configuration value
setting.
``under``, and ``over`` arguments are ignored when an explicit tween
chain is specified using the ``pyramid.tweens`` configuration value.
For more information, see :ref:`registering_tweens`.
"""
return self._add_tween(tween_factory, under=under, over=over,
explicit=False)
@action_method
def _add_tween(self, tween_factory, under=None, over=None, explicit=False):
if not isinstance(tween_factory, string_types):
raise ConfigurationError(
'The "tween_factory" argument to add_tween must be a '
'dotted name to a globally importable object, not %r' %
tween_factory)
name = tween_factory
if name in (MAIN, INGRESS):
raise ConfigurationError('%s is a reserved tween name' % name)
tween_factory = self.maybe_dotted(tween_factory)
def is_string_or_iterable(v):
if isinstance(v, string_types):
return True
if hasattr(v, '__iter__'):
return True
for t, p in [('over', over), ('under', under)]:
if p is not None:
if not is_string_or_iterable(p):
raise ConfigurationError(
'"%s" must be a string or iterable, not %s' % (t, p))
if over is INGRESS or is_nonstr_iter(over) and INGRESS in over:
raise ConfigurationError('%s cannot be over INGRESS' % name)
if under is MAIN or is_nonstr_iter(under) and MAIN in under:
raise ConfigurationError('%s cannot be under MAIN' % name)
registry = self.registry
introspectables = []
tweens = registry.queryUtility(ITweens)
if tweens is None:
tweens = Tweens()
registry.registerUtility(tweens, ITweens)
ex_intr = self.introspectable('tweens',
('tween', EXCVIEW, False),
EXCVIEW,
'implicit tween')
ex_intr['name'] = EXCVIEW
ex_intr['factory'] = excview_tween_factory
ex_intr['type'] = 'implicit'
ex_intr['under'] = None
ex_intr['over'] = MAIN
introspectables.append(ex_intr)
tweens.add_implicit(EXCVIEW, excview_tween_factory, over=MAIN)
def register():
if explicit:
tweens.add_explicit(name, tween_factory)
else:
tweens.add_implicit(name, tween_factory, under=under, over=over)
discriminator = ('tween', name, explicit)
tween_type = explicit and 'explicit' or 'implicit'
intr = self.introspectable('tweens',
discriminator,
name,
'%s tween' % tween_type)
intr['name'] = name
intr['factory'] = tween_factory
intr['type'] = tween_type
intr['under'] = under
intr['over'] = over
introspectables.append(intr)
self.action(discriminator, register, introspectables=introspectables)
@implementer(ITweens)
class Tweens(object):
def __init__(self):
self.sorter = TopologicalSorter(
default_before=None,
default_after=INGRESS,
first=INGRESS,
last=MAIN)
self.explicit = []
def add_explicit(self, name, factory):
self.explicit.append((name, factory))
def add_implicit(self, name, factory, under=None, over=None):
self.sorter.add(name, factory, after=under, before=over)
def implicit(self):
return self.sorter.sorted()
def __call__(self, handler, registry):
if self.explicit:
use = self.explicit
else:
use = self.implicit()
for name, factory in use[::-1]:
handler = factory(handler, registry)
return handler
pyramid-1.4.5/pyramid/config/__init__.py 0000664 0001750 0001750 00000140600 12210154276 017506 0 ustar takaki takaki import inspect
import itertools
import logging
import operator
import os
import sys
import warnings
import venusian
from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
from pyramid.interfaces import (
IDebugLogger,
IExceptionResponse,
IPredicateList,
PHASE1_CONFIG,
)
from pyramid.asset import resolve_asset_spec
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.compat import (
text_,
reraise,
string_types,
)
from pyramid.events import ApplicationCreated
from pyramid.exceptions import (
ConfigurationConflictError,
ConfigurationError,
ConfigurationExecutionError,
)
from pyramid.httpexceptions import default_exceptionresponse_view
from pyramid.path import (
caller_package,
package_of,
)
from pyramid.registry import (
Introspectable,
Introspector,
Registry,
undefer,
)
from pyramid.router import Router
from pyramid.settings import aslist
from pyramid.threadlocal import manager
from pyramid.util import (
WeakOrderedSet,
object_description,
)
from pyramid.config.adapters import AdaptersConfiguratorMixin
from pyramid.config.assets import AssetsConfiguratorMixin
from pyramid.config.factories import FactoriesConfiguratorMixin
from pyramid.config.i18n import I18NConfiguratorMixin
from pyramid.config.rendering import DEFAULT_RENDERERS
from pyramid.config.rendering import RenderingConfiguratorMixin
from pyramid.config.routes import RoutesConfiguratorMixin
from pyramid.config.security import SecurityConfiguratorMixin
from pyramid.config.settings import SettingsConfiguratorMixin
from pyramid.config.testing import TestingConfiguratorMixin
from pyramid.config.tweens import TweensConfiguratorMixin
from pyramid.config.util import PredicateList
from pyramid.config.views import ViewsConfiguratorMixin
from pyramid.config.zca import ZCAConfiguratorMixin
from pyramid.path import DottedNameResolver
from pyramid.util import (
action_method,
ActionInfo,
)
empty = text_('')
_marker = object()
ConfigurationError = ConfigurationError # pyflakes
class Configurator(
TestingConfiguratorMixin,
TweensConfiguratorMixin,
SecurityConfiguratorMixin,
ViewsConfiguratorMixin,
RoutesConfiguratorMixin,
ZCAConfiguratorMixin,
I18NConfiguratorMixin,
RenderingConfiguratorMixin,
AssetsConfiguratorMixin,
SettingsConfiguratorMixin,
FactoriesConfiguratorMixin,
AdaptersConfiguratorMixin,
):
"""
A Configurator is used to configure a :app:`Pyramid`
:term:`application registry`.
The Configurator accepts a number of arguments: ``registry``,
``package``, ``settings``, ``root_factory``, ``authentication_policy``,
``authorization_policy``, ``renderers``, ``debug_logger``,
``locale_negotiator``, ``request_factory``, ``renderer_globals_factory``,
``default_permission``, ``session_factory``, ``default_view_mapper``,
``autocommit``, ``exceptionresponse_view`` and ``route_prefix``.
If the ``registry`` argument is passed as a non-``None`` value, it must
be an instance of the :class:`pyramid.registry.Registry` class
representing the registry to configure. If ``registry`` is ``None``, the
configurator will create a :class:`pyramid.registry.Registry` instance
itself; it will also perform some default configuration that would not
otherwise be done. After its construction, the configurator may be used
to add further configuration to the registry.
.. warning:: If a ``registry`` is passed to the Configurator
constructor, all other constructor arguments except ``package``
are ignored.
If the ``package`` argument is passed, it must be a reference to a Python
:term:`package` (e.g. ``sys.modules['thepackage']``) or a :term:`dotted
Python name` to the same. This value is used as a basis to convert
relative paths passed to various configuration methods, such as methods
which accept a ``renderer`` argument, into absolute paths. If ``None``
is passed (the default), the package is assumed to be the Python package
in which the *caller* of the ``Configurator`` constructor lives.
If the ``settings`` argument is passed, it should be a Python dictionary
representing the :term:`deployment settings` for this application. These
are later retrievable using the
:attr:`pyramid.registry.Registry.settings` attribute (aka
``request.registry.settings``).
If the ``root_factory`` argument is passed, it should be an object
representing the default :term:`root factory` for your application or a
:term:`dotted Python name` to the same. If it is ``None``, a default
root factory will be used.
If ``authentication_policy`` is passed, it should be an instance
of an :term:`authentication policy` or a :term:`dotted Python
name` to the same.
If ``authorization_policy`` is passed, it should be an instance of
an :term:`authorization policy` or a :term:`dotted Python name` to
the same.
.. note:: A ``ConfigurationError`` will be raised when an
authorization policy is supplied without also supplying an
authentication policy (authorization requires authentication).
If ``renderers`` is passed, it should be a list of tuples
representing a set of :term:`renderer` factories which should be
configured into this application (each tuple representing a set of
positional values that should be passed to
:meth:`pyramid.config.Configurator.add_renderer`). If
it is not passed, a default set of renderer factories is used.
If ``debug_logger`` is not passed, a default debug logger that logs to a
logger will be used (the logger name will be the package name of the
*caller* of this configurator). If it is passed, it should be an
instance of the :class:`logging.Logger` (PEP 282) standard library class
or a Python logger name. The debug logger is used by :app:`Pyramid`
itself to log warnings and authorization debugging information.
If ``locale_negotiator`` is passed, it should be a :term:`locale
negotiator` implementation or a :term:`dotted Python name` to
same. See :ref:`custom_locale_negotiator`.
If ``request_factory`` is passed, it should be a :term:`request
factory` implementation or a :term:`dotted Python name` to the same.
See :ref:`changing_the_request_factory`. By default it is ``None``,
which means use the default request factory.
If ``renderer_globals_factory`` is passed, it should be a :term:`renderer
globals` factory implementation or a :term:`dotted Python name` to the
same. See :ref:`adding_renderer_globals`. By default, it is ``None``,
which means use no renderer globals factory.
.. warning::
as of Pyramid 1.1, ``renderer_globals_factory`` is deprecated. Instead,
use a BeforeRender event subscriber as per :ref:`beforerender_event`.
If ``default_permission`` is passed, it should be a
:term:`permission` string to be used as the default permission for
all view configuration registrations performed against this
Configurator. An example of a permission string:``'view'``.
Adding a default permission makes it unnecessary to protect each
view configuration with an explicit permission, unless your
application policy requires some exception for a particular view.
By default, ``default_permission`` is ``None``, meaning that view
configurations which do not explicitly declare a permission will
always be executable by entirely anonymous users (any
authorization policy in effect is ignored). See also
:ref:`setting_a_default_permission`.
If ``session_factory`` is passed, it should be an object which
implements the :term:`session factory` interface. If a nondefault
value is passed, the ``session_factory`` will be used to create a
session object when ``request.session`` is accessed. Note that
the same outcome can be achieved by calling
:meth:`pyramid.config.Configurator.set_session_factory`. By
default, this argument is ``None``, indicating that no session
factory will be configured (and thus accessing ``request.session``
will throw an error) unless ``set_session_factory`` is called later
during configuration.
If ``autocommit`` is ``True``, every method called on the configurator
will cause an immediate action, and no configuration conflict detection
will be used. If ``autocommit`` is ``False``, most methods of the
configurator will defer their action until
:meth:`pyramid.config.Configurator.commit` is called. When
:meth:`pyramid.config.Configurator.commit` is called, the actions implied
by the called methods will be checked for configuration conflicts unless
``autocommit`` is ``True``. If a conflict is detected a
``ConfigurationConflictError`` will be raised. Calling
:meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final
commit.
If ``default_view_mapper`` is passed, it will be used as the default
:term:`view mapper` factory for view configurations that don't otherwise
specify one (see :class:`pyramid.interfaces.IViewMapperFactory`). If a
default_view_mapper is not passed, a superdefault view mapper will be
used.
If ``exceptionresponse_view`` is passed, it must be a :term:`view
callable` or ``None``. If it is a view callable, it will be used as an
exception view callable when an :term:`exception response` is raised. If
``exceptionresponse_view`` is ``None``, no exception response view will
be registered, and all raised exception responses will be bubbled up to
Pyramid's caller. By
default, the ``pyramid.httpexceptions.default_exceptionresponse_view``
function is used as the ``exceptionresponse_view``. This argument is new
in Pyramid 1.1.
If ``route_prefix`` is passed, all routes added with
:meth:`pyramid.config.Configurator.add_route` will have the specified path
prepended to their pattern. This parameter is new in Pyramid 1.2.
If ``introspection`` is passed, it must be a boolean value. If it's
``True``, introspection values during actions will be kept for use
for tools like the debug toolbar. If it's ``False``, introspection
values provided by registrations will be ignored. By default, it is
``True``. This parameter is new as of Pyramid 1.3.
"""
manager = manager # for testing injection
venusian = venusian # for testing injection
_ainfo = None
basepath = None
includepath = ()
info = ''
object_description = staticmethod(object_description)
introspectable = Introspectable
inspect = inspect
def __init__(self,
registry=None,
package=None,
settings=None,
root_factory=None,
authentication_policy=None,
authorization_policy=None,
renderers=None,
debug_logger=None,
locale_negotiator=None,
request_factory=None,
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
default_view_mapper=None,
autocommit=False,
exceptionresponse_view=default_exceptionresponse_view,
route_prefix=None,
introspection=True,
):
if package is None:
package = caller_package()
name_resolver = DottedNameResolver(package)
self.name_resolver = name_resolver
self.package_name = name_resolver.get_package_name()
self.package = name_resolver.get_package()
self.registry = registry
self.autocommit = autocommit
self.route_prefix = route_prefix
self.introspection = introspection
if registry is None:
registry = Registry(self.package_name)
self.registry = registry
self.setup_registry(
settings=settings,
root_factory=root_factory,
authentication_policy=authentication_policy,
authorization_policy=authorization_policy,
renderers=renderers,
debug_logger=debug_logger,
locale_negotiator=locale_negotiator,
request_factory=request_factory,
renderer_globals_factory=renderer_globals_factory,
default_permission=default_permission,
session_factory=session_factory,
default_view_mapper=default_view_mapper,
exceptionresponse_view=exceptionresponse_view,
)
def setup_registry(self,
settings=None,
root_factory=None,
authentication_policy=None,
authorization_policy=None,
renderers=None,
debug_logger=None,
locale_negotiator=None,
request_factory=None,
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
default_view_mapper=None,
exceptionresponse_view=default_exceptionresponse_view,
):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial setup is performed
against the registry. This is because the registry you pass in may
have already been initialized for use under :app:`Pyramid` via a
different configurator. However, in some circumstances (such as when
you want to use a global registry instead of a registry created as a
result of the Configurator constructor), or when you want to reset
the initial setup of a registry, you *do* want to explicitly
initialize the registry associated with a Configurator for use under
:app:`Pyramid`. Use ``setup_registry`` to do this initialization.
``setup_registry`` configures settings, a root factory, security
policies, renderers, a debug logger, a locale negotiator, and various
other settings using the configurator's current registry, as per the
descriptions in the Configurator constructor."""
registry = self.registry
self._fix_registry()
self._set_settings(settings)
self._register_response_adapters()
if isinstance(debug_logger, string_types):
debug_logger = logging.getLogger(debug_logger)
if debug_logger is None:
debug_logger = logging.getLogger(self.package_name)
registry.registerUtility(debug_logger, IDebugLogger)
for name, renderer in DEFAULT_RENDERERS:
self.add_renderer(name, renderer)
self.add_default_view_predicates()
self.add_default_route_predicates()
if exceptionresponse_view is not None:
exceptionresponse_view = self.maybe_dotted(exceptionresponse_view)
self.add_view(exceptionresponse_view, context=IExceptionResponse)
self.add_view(exceptionresponse_view,context=WebobWSGIHTTPException)
# commit below because:
#
# - the default exceptionresponse_view requires the superdefault view
# mapper, so we need to configure it before adding default_view_mapper
#
# - superdefault renderers should be overrideable without requiring
# the user to commit before calling config.add_renderer
self.commit()
# self.commit() should not be called after this point because the
# following registrations should be treated as analogues of methods
# called by the user after configurator construction. Rationale:
# user-supplied implementations should be preferred rather than
# add-on author implementations with the help of automatic conflict
# resolution.
if authentication_policy and not authorization_policy:
authorization_policy = ACLAuthorizationPolicy() # default
if authorization_policy:
self.set_authorization_policy(authorization_policy)
if authentication_policy:
self.set_authentication_policy(authentication_policy)
if default_view_mapper is not None:
self.set_view_mapper(default_view_mapper)
if renderers:
for name, renderer in renderers:
self.add_renderer(name, renderer)
if root_factory is not None:
self.set_root_factory(root_factory)
if locale_negotiator:
self.set_locale_negotiator(locale_negotiator)
if request_factory:
self.set_request_factory(request_factory)
if renderer_globals_factory:
warnings.warn(
'Passing ``renderer_globals_factory`` as a Configurator '
'constructor parameter is deprecated as of Pyramid 1.1. '
'Use a BeforeRender event subscriber as documented in the '
'"Hooks" chapter of the Pyramid narrative documentation '
'instead',
DeprecationWarning,
2)
self.set_renderer_globals_factory(renderer_globals_factory,
warn=False)
if default_permission:
self.set_default_permission(default_permission)
if session_factory is not None:
self.set_session_factory(session_factory)
tweens = aslist(registry.settings.get('pyramid.tweens', []))
for factory in tweens:
self._add_tween(factory, explicit=True)
includes = aslist(registry.settings.get('pyramid.includes', []))
for inc in includes:
self.include(inc)
def _make_spec(self, path_or_spec):
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_asset_spec(path_or_spec, self.package_name)
def _fix_registry(self):
""" Fix up a ZCA component registry that is not a
pyramid.registry.Registry by adding analogues of ``has_listeners``,
``notify``, ``queryAdapterOrSelf``, and ``registerSelfAdapter``
through monkey-patching."""
_registry = self.registry
if not hasattr(_registry, 'notify'):
def notify(*events):
[ _ for _ in _registry.subscribers(events, None) ]
_registry.notify = notify
if not hasattr(_registry, 'has_listeners'):
_registry.has_listeners = True
if not hasattr(_registry, 'queryAdapterOrSelf'):
def queryAdapterOrSelf(object, interface, default=None):
if not interface.providedBy(object):
return _registry.queryAdapter(object, interface,
default=default)
return object
_registry.queryAdapterOrSelf = queryAdapterOrSelf
if not hasattr(_registry, 'registerSelfAdapter'):
def registerSelfAdapter(required=None, provided=None,
name=empty, info=empty, event=True):
return _registry.registerAdapter(lambda x: x,
required=required,
provided=provided, name=name,
info=info, event=event)
_registry.registerSelfAdapter = registerSelfAdapter
# API
def _get_introspector(self):
introspector = getattr(self.registry, 'introspector', _marker)
if introspector is _marker:
introspector = Introspector()
self._set_introspector(introspector)
return introspector
def _set_introspector(self, introspector):
self.registry.introspector = introspector
def _del_introspector(self):
del self.registry.introspector
introspector = property(
_get_introspector, _set_introspector, _del_introspector
)
def get_predlist(self, name):
predlist = self.registry.queryUtility(IPredicateList, name=name)
if predlist is None:
predlist = PredicateList()
self.registry.registerUtility(predlist, IPredicateList, name=name)
return predlist
def _add_predicate(self, type, name, factory, weighs_more_than=None,
weighs_less_than=None):
discriminator = ('%s predicate' % type, name)
intr = self.introspectable(
'%s predicates' % type,
discriminator,
'%s predicate named %s' % (type, name),
'%s predicate' % type)
intr['name'] = name
intr['factory'] = factory
intr['weighs_more_than'] = weighs_more_than
intr['weighs_less_than'] = weighs_less_than
def register():
predlist = self.get_predlist(type)
predlist.add(name, factory, weighs_more_than=weighs_more_than,
weighs_less_than=weighs_less_than)
self.action(discriminator, register, introspectables=(intr,),
order=PHASE1_CONFIG) # must be registered early
@property
def action_info(self):
info = self.info # usually a ZCML action (ParserInfo) if self.info
if not info:
# Try to provide more accurate info for conflict reports
if self._ainfo:
info = self._ainfo[0]
else:
info = ActionInfo(None, 0, '', '')
return info
def action(self, discriminator, callable=None, args=(), kw=None, order=0,
introspectables=(), **extra):
""" Register an action which will be executed when
:meth:`pyramid.config.Configurator.commit` is called (or executed
immediately if ``autocommit`` is ``True``).
.. warning:: This method is typically only used by :app:`Pyramid`
framework extension authors, not by :app:`Pyramid` application
developers.
The ``discriminator`` uniquely identifies the action. It must be
given, but it can be ``None``, to indicate that the action never
conflicts. It must be a hashable value.
The ``callable`` is a callable object which performs the task
associated with the action when the action is executed. It is
optional.
``args`` and ``kw`` are tuple and dict objects respectively, which
are passed to ``callable`` when this action is executed. Both are
optional.
``order`` is a grouping mechanism; an action with a lower order will
be executed before an action with a higher order (has no effect when
autocommit is ``True``).
``introspectables`` is a sequence of :term:`introspectable` objects
(or the empty sequence if no introspectable objects are associated
with this action). If this configurator's ``introspection``
attribute is ``False``, these introspectables will be ignored.
``extra`` provides a facility for inserting extra keys and values
into an action dictionary.
"""
# catch nonhashable discriminators here; most unit tests use
# autocommit=False, which won't catch unhashable discriminators
assert hash(discriminator)
if kw is None:
kw = {}
autocommit = self.autocommit
action_info = self.action_info
if not self.introspection:
# if we're not introspecting, ignore any introspectables passed
# to us
introspectables = ()
if autocommit:
# callables can depend on the side effects of resolving a
# deferred discriminator
undefer(discriminator)
if callable is not None:
callable(*args, **kw)
for introspectable in introspectables:
introspectable.register(self.introspector, action_info)
else:
action = extra
action.update(
dict(
discriminator=discriminator,
callable=callable,
args=args,
kw=kw,
order=order,
info=action_info,
includepath=self.includepath,
introspectables=introspectables,
)
)
self.action_state.action(**action)
def _get_action_state(self):
registry = self.registry
try:
state = registry.action_state
except AttributeError:
state = ActionState()
registry.action_state = state
return state
def _set_action_state(self, state):
self.registry.action_state = state
action_state = property(_get_action_state, _set_action_state)
_ctx = action_state # bw compat
def commit(self):
""" Commit any pending configuration actions. If a configuration
conflict is detected in the pending configuration actions, this method
will raise a :exc:`ConfigurationConflictError`; within the traceback
of this error will be information about the source of the conflict,
usually including file names and line numbers of the cause of the
configuration conflicts."""
self.action_state.execute_actions(introspector=self.introspector)
self.action_state = ActionState() # old actions have been processed
def include(self, callable, route_prefix=None):
"""Include a configuration callables, to support imperative
application extensibility.
.. warning:: In versions of :app:`Pyramid` prior to 1.2, this
function accepted ``*callables``, but this has been changed
to support only a single callable.
A configuration callable should be a callable that accepts a single
argument named ``config``, which will be an instance of a
:term:`Configurator` (be warned that it will not be the same
configurator instance on which you call this method, however). The
code which runs as the result of calling the callable should invoke
methods on the configurator passed to it which add configuration
state. The return value of a callable will be ignored.
Values allowed to be presented via the ``callable`` argument to
this method: any callable Python object or any :term:`dotted Python
name` which resolves to a callable Python object. It may also be a
Python :term:`module`, in which case, the module will be searched for
a callable named ``includeme``, which will be treated as the
configuration callable.
For example, if the ``includeme`` function below lives in a module
named ``myapp.myconfig``:
.. code-block:: python
:linenos:
# myapp.myconfig module
def my_view(request):
from pyramid.response import Response
return Response('OK')
def includeme(config):
config.add_view(my_view)
You might cause it be included within your Pyramid application like
so:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator()
config.include('myapp.myconfig.includeme')
Because the function is named ``includeme``, the function name can
also be omitted from the dotted name reference:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator()
config.include('myapp.myconfig')
Included configuration statements will be overridden by local
configuration statements if an included callable causes a
configuration conflict by registering something with the same
configuration parameters.
If the ``route_prefix`` is supplied, it must be a string. Any calls
to :meth:`pyramid.config.Configurator.add_route` within the included
callable will have their pattern prefixed with the value of
``route_prefix``. This can be used to help mount a set of routes at a
different location than the included callable's author intended while
still maintaining the same route names. For example:
.. code-block:: python
:linenos:
from pyramid.config import Configurator
def included(config):
config.add_route('show_users', '/show')
def main(global_config, **settings):
config = Configurator()
config.include(included, route_prefix='/users')
In the above configuration, the ``show_users`` route will have an
effective route pattern of ``/users/show``, instead of ``/show``
because the ``route_prefix`` argument will be prepended to the
pattern.
The ``route_prefix`` parameter is new as of Pyramid 1.2.
"""
# """ <-- emacs
action_state = self.action_state
if route_prefix is None:
route_prefix = ''
old_route_prefix = self.route_prefix
if old_route_prefix is None:
old_route_prefix = ''
route_prefix = '%s/%s' % (
old_route_prefix.rstrip('/'),
route_prefix.lstrip('/')
)
route_prefix = route_prefix.strip('/')
if not route_prefix:
route_prefix = None
c = self.maybe_dotted(callable)
module = self.inspect.getmodule(c)
if module is c:
try:
c = getattr(module, 'includeme')
except AttributeError:
raise ConfigurationError(
"module %r has no attribute 'includeme'" % (module.__name__)
)
spec = module.__name__ + ':' + c.__name__
sourcefile = self.inspect.getsourcefile(c)
if sourcefile is None:
raise ConfigurationError(
'No source file for module %r (.py file must exist, '
'refusing to use orphan .pyc or .pyo file).' % module.__name__)
if action_state.processSpec(spec):
configurator = self.__class__(
registry=self.registry,
package=package_of(module),
autocommit=self.autocommit,
route_prefix=route_prefix,
)
configurator.basepath = os.path.dirname(sourcefile)
configurator.includepath = self.includepath + (spec,)
c(configurator)
def add_directive(self, name, directive, action_wrap=True):
"""
Add a directive method to the configurator.
.. warning:: This method is typically only used by :app:`Pyramid`
framework extension authors, not by :app:`Pyramid` application
developers.
Framework extenders can add directive methods to a configurator by
instructing their users to call ``config.add_directive('somename',
'some.callable')``. This will make ``some.callable`` accessible as
``config.somename``. ``some.callable`` should be a function which
accepts ``config`` as a first argument, and arbitrary positional and
keyword arguments following. It should use config.action as
necessary to perform actions. Directive methods can then be invoked
like 'built-in' directives such as ``add_view``, ``add_route``, etc.
The ``action_wrap`` argument should be ``True`` for directives which
perform ``config.action`` with potentially conflicting
discriminators. ``action_wrap`` will cause the directive to be
wrapped in a decorator which provides more accurate conflict
cause information.
``add_directive`` does not participate in conflict detection, and
later calls to ``add_directive`` will override earlier calls.
"""
c = self.maybe_dotted(directive)
if not hasattr(self.registry, '_directives'):
self.registry._directives = {}
self.registry._directives[name] = (c, action_wrap)
def __getattr__(self, name):
# allow directive extension names to work
directives = getattr(self.registry, '_directives', {})
c = directives.get(name)
if c is None:
raise AttributeError(name)
c, action_wrap = c
if action_wrap:
c = action_method(c)
# Create a bound method (works on both Py2 and Py3)
# http://stackoverflow.com/a/1015405/209039
m = c.__get__(self, self.__class__)
return m
def with_package(self, package):
""" Return a new Configurator instance with the same registry
as this configurator using the package supplied as the
``package`` argument to the new configurator. ``package`` may
be an actual Python package object or a :term:`dotted Python name`
representing a package."""
configurator = self.__class__(
registry=self.registry,
package=package,
autocommit=self.autocommit,
route_prefix=self.route_prefix,
introspection=self.introspection,
)
configurator.basepath = self.basepath
configurator.includepath = self.includepath
configurator.info = self.info
return configurator
def maybe_dotted(self, dotted):
""" Resolve the :term:`dotted Python name` ``dotted`` to a
global Python object. If ``dotted`` is not a string, return
it without attempting to do any name resolution. If
``dotted`` is a relative dotted name (e.g. ``.foo.bar``,
consider it relative to the ``package`` argument supplied to
this Configurator's constructor."""
return self.name_resolver.maybe_resolve(dotted)
def absolute_asset_spec(self, relative_spec):
""" Resolve the potentially relative :term:`asset
specification` string passed as ``relative_spec`` into an
absolute asset specification string and return the string.
Use the ``package`` of this configurator as the package to
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, string_types):
return relative_spec
return self._make_spec(relative_spec)
absolute_resource_spec = absolute_asset_spec # b/w compat forever
def begin(self, request=None):
""" Indicate that application or test configuration has begun.
This pushes a dictionary containing the :term:`application
registry` implied by ``registry`` attribute of this
configurator and the :term:`request` implied by the
``request`` argument on to the :term:`thread local` stack
consulted by various :mod:`pyramid.threadlocal` API
functions."""
self.manager.push({'registry':self.registry, 'request':request})
def end(self):
""" Indicate that application or test configuration has ended.
This pops the last value pushed on to the :term:`thread local`
stack (usually by the ``begin`` method) and returns that
value.
"""
return self.manager.pop()
# this is *not* an action method (uses caller_package)
def scan(self, package=None, categories=None, onerror=None, ignore=None,
**kw):
"""Scan a Python package and any of its subpackages for objects
marked with :term:`configuration decoration` such as
:class:`pyramid.view.view_config`. Any decorated object found will
influence the current configuration state.
The ``package`` argument should be a Python :term:`package` or module
object (or a :term:`dotted Python name` which refers to such a
package or module). If ``package`` is ``None``, the package of the
*caller* is used.
The ``categories`` argument, if provided, should be the
:term:`Venusian` 'scan categories' to use during scanning. Providing
this argument is not often necessary; specifying scan categories is
an extremely advanced usage. By default, ``categories`` is ``None``
which will execute *all* Venusian decorator callbacks including
:app:`Pyramid`-related decorators such as
:class:`pyramid.view.view_config`. See the :term:`Venusian`
documentation for more information about limiting a scan by using an
explicit set of categories.
The ``onerror`` argument, if provided, should be a Venusian
``onerror`` callback function. The onerror function is passed to
:meth:`venusian.Scanner.scan` to influence error behavior when an
exception is raised during the scanning process. See the
:term:`Venusian` documentation for more information about ``onerror``
callbacks.
The ``ignore`` argument, if provided, should be a Venusian ``ignore``
value. Providing an ``ignore`` argument allows the scan to ignore
particular modules, packages, or global objects during a scan.
``ignore`` can be a string or a callable, or a list containing
strings or callables. The simplest usage of ``ignore`` is to provide
a module or package by providing a full path to its dotted name. For
example: ``config.scan(ignore='my.module.subpackage')`` would ignore
the ``my.module.subpackage`` package during a scan, which would
prevent the subpackage and any of its submodules from being imported
and scanned. See the :term:`Venusian` documentation for more
information about the ``ignore`` argument.
.. note:: the ``ignore`` argument is new in Pyramid 1.3.
To perform a ``scan``, Pyramid creates a Venusian ``Scanner`` object.
The ``kw`` argument represents a set of keyword arguments to pass to
the Venusian ``Scanner`` object's constructor. See the
:term:`venusian` documentation (its ``Scanner`` class) for more
information about the constructor. By default, the only keyword
arguments passed to the Scanner constructor are ``{'config':self}``
where ``self`` is this configurator object. This services the
requirement of all built-in Pyramid decorators, but extension systems
may require additional arguments. Providing this argument is not
often necessary; it's an advanced usage.
.. note:: the ``**kw`` argument is new in Pyramid 1.1
"""
package = self.maybe_dotted(package)
if package is None: # pragma: no cover
package = caller_package()
ctorkw = {'config':self}
ctorkw.update(kw)
scanner = self.venusian.Scanner(**ctorkw)
scanner.scan(package, categories=categories, onerror=onerror,
ignore=ignore)
def make_wsgi_app(self):
""" Commits any pending configuration statements, sends a
:class:`pyramid.events.ApplicationCreated` event to all listeners,
adds this configuration's registry to
:attr:`pyramid.config.global_registries`, and returns a
:app:`Pyramid` WSGI application representing the committed
configuration state."""
self.commit()
app = Router(self.registry)
# Allow tools like "pshell development.ini" to find the 'last'
# registry configured.
global_registries.add(self.registry)
# Push the registry on to the stack in case any code that depends on
# the registry threadlocal APIs used in listeners subscribed to the
# IApplicationCreated event.
self.manager.push({'registry':self.registry, 'request':None})
try:
self.registry.notify(ApplicationCreated(app))
finally:
self.manager.pop()
return app
# this class is licensed under the ZPL (stolen from Zope)
class ActionState(object):
def __init__(self):
# NB "actions" is an API, dep'd upon by pyramid_zcml's load_zcml func
self.actions = []
self._seen_files = set()
def processSpec(self, spec):
"""Check whether a callable needs to be processed. The ``spec``
refers to a unique identifier for the callable.
Return True if processing is needed and False otherwise. If
the callable needs to be processed, it will be marked as
processed, assuming that the caller will procces the callable if
it needs to be processed.
"""
if spec in self._seen_files:
return False
self._seen_files.add(spec)
return True
def action(self, discriminator, callable=None, args=(), kw=None, order=0,
includepath=(), info=None, introspectables=(), **extra):
"""Add an action with the given discriminator, callable and arguments
"""
if kw is None:
kw = {}
action = extra
action.update(
dict(
discriminator=discriminator,
callable=callable,
args=args,
kw=kw,
includepath=includepath,
info=info,
order=order,
introspectables=introspectables,
)
)
self.actions.append(action)
def execute_actions(self, clear=True, introspector=None):
"""Execute the configuration actions
This calls the action callables after resolving conflicts
For example:
>>> output = []
>>> def f(*a, **k):
... output.append(('f', a, k))
>>> context = ActionState()
>>> context.actions = [
... (1, f, (1,)),
... (1, f, (11,), {}, ('x', )),
... (2, f, (2,)),
... ]
>>> context.execute_actions()
>>> output
[('f', (1,), {}), ('f', (2,), {})]
If the action raises an error, we convert it to a
ConfigurationExecutionError.
>>> output = []
>>> def bad():
... bad.xxx
>>> context.actions = [
... (1, f, (1,)),
... (1, f, (11,), {}, ('x', )),
... (2, f, (2,)),
... (3, bad, (), {}, (), 'oops')
... ]
>>> try:
... v = context.execute_actions()
... except ConfigurationExecutionError, v:
... pass
>>> print v
exceptions.AttributeError: 'function' object has no attribute 'xxx'
in:
oops
Note that actions executed before the error still have an effect:
>>> output
[('f', (1,), {}), ('f', (2,), {})]
"""
try:
for action in resolveConflicts(self.actions):
callable = action['callable']
args = action['args']
kw = action['kw']
info = action['info']
# we use "get" below in case an action was added via a ZCML
# directive that did not know about introspectables
introspectables = action.get('introspectables', ())
try:
if callable is not None:
callable(*args, **kw)
except (KeyboardInterrupt, SystemExit): # pragma: no cover
raise
except:
t, v, tb = sys.exc_info()
try:
reraise(ConfigurationExecutionError,
ConfigurationExecutionError(t, v, info),
tb)
finally:
del t, v, tb
if introspector is not None:
for introspectable in introspectables:
introspectable.register(introspector, info)
finally:
if clear:
del self.actions[:]
# this function is licensed under the ZPL (stolen from Zope)
def resolveConflicts(actions):
"""Resolve conflicting actions
Given an actions list, identify and try to resolve conflicting actions.
Actions conflict if they have the same non-None discriminator.
Conflicting actions can be resolved if the include path of one of
the actions is a prefix of the includepaths of the other
conflicting actions and is unequal to the include paths in the
other conflicting actions.
"""
def orderandpos(v):
n, v = v
if not isinstance(v, dict):
# old-style tuple action
v = expand_action(*v)
return (v['order'] or 0, n)
sactions = sorted(enumerate(actions), key=orderandpos)
def orderonly(v):
n, v = v
if not isinstance(v, dict):
# old-style tuple action
v = expand_action(*v)
return v['order'] or 0
for order, actiongroup in itertools.groupby(sactions, orderonly):
# "order" is an integer grouping. Actions in a lower order will be
# executed before actions in a higher order. All of the actions in
# one grouping will be executed (its callable, if any will be called)
# before any of the actions in the next.
unique = {}
output = []
for i, action in actiongroup:
# Within an order, actions are executed sequentially based on
# original action ordering ("i").
if not isinstance(action, dict):
# old-style tuple action
action = expand_action(*action)
# "ainfo" is a tuple of (order, i, action) where "order" is a
# user-supplied grouping, "i" is an integer expressing the relative
# position of this action in the action list being resolved, and
# "action" is an action dictionary. The purpose of an ainfo is to
# associate an "order" and an "i" with a particular action; "order"
# and "i" exist for sorting purposes after conflict resolution.
ainfo = (order, i, action)
discriminator = undefer(action['discriminator'])
action['discriminator'] = discriminator
if discriminator is None:
# The discriminator is None, so this action can never conflict.
# We can add it directly to the result.
output.append(ainfo)
continue
L = unique.setdefault(discriminator, [])
L.append(ainfo)
# Check for conflicts
conflicts = {}
for discriminator, ainfos in unique.items():
# We use (includepath, order, i) as a sort key because we need to
# sort the actions by the paths so that the shortest path with a
# given prefix comes first. The "first" action is the one with the
# shortest include path. We break sorting ties using "order", then
# "i".
def bypath(ainfo):
path, order, i = ainfo[2]['includepath'], ainfo[0], ainfo[1]
return path, order, i
ainfos.sort(key=bypath)
ainfo, rest = ainfos[0], ainfos[1:]
output.append(ainfo)
_, _, action = ainfo
basepath, baseinfo, discriminator = (
action['includepath'],
action['info'],
action['discriminator'],
)
for _, _, action in rest:
includepath = action['includepath']
# Test whether path is a prefix of opath
if (includepath[:len(basepath)] != basepath # not a prefix
or includepath == basepath):
L = conflicts.setdefault(discriminator, [baseinfo])
L.append(action['info'])
if conflicts:
raise ConfigurationConflictError(conflicts)
# sort conflict-resolved actions by (order, i) and yield them one by one
for a in [x[2] for x in sorted(output, key=operator.itemgetter(0, 1))]:
yield a
def expand_action(discriminator, callable=None, args=(), kw=None,
includepath=(), info=None, order=0, introspectables=()):
if kw is None:
kw = {}
return dict(
discriminator=discriminator,
callable=callable,
args=args,
kw=kw,
includepath=includepath,
info=info,
order=order,
introspectables=introspectables,
)
global_registries = WeakOrderedSet()
pyramid-1.4.5/pyramid/config/routes.py 0000664 0001750 0001750 00000057476 12210154276 017312 0 ustar takaki takaki import warnings
from pyramid.interfaces import (
IRequest,
IRouteRequest,
IRoutesMapper,
PHASE2_CONFIG,
)
from pyramid.exceptions import ConfigurationError
from pyramid.registry import predvalseq
from pyramid.request import route_request_iface
from pyramid.urldispatch import RoutesMapper
from pyramid.config.util import (
action_method,
as_sorted_tuple,
)
import pyramid.config.predicates
class RoutesConfiguratorMixin(object):
@action_method
def add_route(self,
name,
pattern=None,
view=None,
view_for=None,
permission=None,
factory=None,
for_=None,
header=None,
xhr=None,
accept=None,
path_info=None,
request_method=None,
request_param=None,
traverse=None,
custom_predicates=(),
view_permission=None,
renderer=None,
view_renderer=None,
view_context=None,
view_attr=None,
use_global_views=False,
path=None,
pregenerator=None,
static=False,
**predicates):
""" Add a :term:`route configuration` to the current
configuration state, as well as possibly a :term:`view
configuration` to be used to specify a :term:`view callable`
that will be invoked when this route matches. The arguments
to this method are divided into *predicate*, *non-predicate*,
and *view-related* types. :term:`Route predicate` arguments
narrow the circumstances in which a route will be match a
request; non-predicate arguments are informational.
Non-Predicate Arguments
name
The name of the route, e.g. ``myroute``. This attribute is
required. It must be unique among all defined routes in a given
application.
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` 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. See
:ref:`the_resource_tree` for more information about root factories.
traverse
If you would like to cause the :term:`context` to be
something other than the :term:`root` object when this route
matches, you can spell a traversal pattern as the
``traverse`` argument. This traversal pattern will be used
as the traversal path: traversal will begin at the root
object implied by this route (either the global root, or the
object returned by the ``factory`` associated with this
route).
The syntax of the ``traverse`` argument is the same as it is
for ``pattern``. For example, if the ``pattern`` provided to
``add_route`` is ``articles/{article}/edit``, and the
``traverse`` argument provided to ``add_route`` is
``/{article}``, when a request comes in that causes the route
to match in such a way that the ``article`` match value is
'1' (when the request URI is ``/articles/1/edit``), the
traversal path will be generated as ``/1``. This means that
the root object's ``__getitem__`` will be called with the
name ``1`` during the traversal phase. If the ``1`` object
exists, it will become the :term:`context` of the request.
:ref:`traversal_chapter` has more information about
traversal.
If the traversal path contains segment marker names which
are not present in the ``pattern`` argument, a runtime error
will occur. The ``traverse`` pattern should not contain
segment markers that do not exist in the ``pattern``
argument.
A similar combining of routing and traversal is available
when a route is matched which contains a ``*traverse``
remainder marker in its pattern (see
:ref:`using_traverse_in_a_route_pattern`). The ``traverse``
argument to add_route allows you to associate route patterns
with an arbitrary traversal path without using a
``*traverse`` remainder marker; instead you can use other
match information.
Note that the ``traverse`` argument to ``add_route`` is
ignored when attached to a route that has a ``*traverse``
remainder marker in its pattern.
pregenerator
This option should be a callable object that implements the
:class:`pyramid.interfaces.IRoutePregenerator` interface. A
:term:`pregenerator` is a callable called by the
:meth:`pyramid.request.Request.route_url` function to augment or
replace the arguments it is passed when generating a URL for the
route. This is a feature not often used directly by applications,
it is meant to be hooked by frameworks that use :app:`Pyramid` as
a base.
use_global_views
When a request matches this route, and view lookup cannot
find a view which has a ``route_name`` predicate argument
that matches the route, try to fall back to using a view
that otherwise matches the context, request, and view name
(but which does not match the route_name predicate).
static
If ``static`` is ``True``, this route will never match an incoming
request; it will only be useful for URL generation. By default,
``static`` is ``False``. See :ref:`static_route_narr`.
.. note:: New in :app:`Pyramid` 1.1.
Predicate Arguments
pattern
The pattern of the route e.g. ``ideas/{idea}``. This
argument is required. See :ref:`route_pattern_syntax`
for information about the syntax of route patterns. If the
pattern doesn't match the current URL, route matching
continues.
.. note::
For backwards compatibility purposes (as of :app:`Pyramid` 1.0), a
``path`` keyword argument passed to this function will be used to
represent the pattern value if the ``pattern`` argument is
``None``. If both ``path`` and ``pattern`` are passed, ``pattern``
wins.
xhr
This value should be either ``True`` or ``False``. If this
value is specified and is ``True``, the :term:`request` must
possess an ``HTTP_X_REQUESTED_WITH`` (aka
``X-Requested-With``) header for this route to match. This
is useful for detecting AJAX requests issued from jQuery,
Prototype and other Javascript libraries. If this predicate
returns ``False``, route matching continues.
request_method
A string representing an HTTP method name, e.g. ``GET``, ``POST``,
``HEAD``, ``DELETE``, ``PUT`` or a tuple of elements containing
HTTP method names. If this argument is not specified, this route
will match if the request has *any* request method. If this
predicate returns ``False``, route matching continues.
.. note:: The ability to pass a tuple of items as
``request_method`` is new as of Pyramid 1.2. Previous
versions allowed only a string.
path_info
This value represents a regular expression pattern that will
be tested against the ``PATH_INFO`` WSGI environment
variable. If the regex matches, this predicate will return
``True``. If this predicate returns ``False``, route
matching continues.
request_param
This value can be any string. A view declaration with this
argument ensures that the associated route will only match
when the request has a key in the ``request.params``
dictionary (an HTTP ``GET`` or ``POST`` variable) that has a
name which matches the supplied value. If the value
supplied as the argument has a ``=`` sign in it,
e.g. ``request_param="foo=123"``, then the key
(``foo``) must both exist in the ``request.params`` dictionary, and
the value must match the right hand side of the expression (``123``)
for the route to "match" the current request. If this predicate
returns ``False``, route matching continues.
header
This argument represents an HTTP header name or a header
name/value pair. If the argument contains a ``:`` (colon),
it will be considered a name/value pair
(e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If
the value contains a colon, the value portion should be a
regular expression. If the value does not contain a colon,
the entire value will be considered to be the header name
(e.g. ``If-Modified-Since``). If the value evaluates to a
header name only without a value, the header specified by
the name must be present in the request for this predicate
to be true. If the value evaluates to a header name/value
pair, the header specified by the name must be present in
the request *and* the regular expression specified as the
value must match the header value. Whether or not the value
represents a header name or a header name/value pair, the
case of the header name is not significant. If this
predicate returns ``False``, route matching continues.
accept
This value represents a match query for one or more
mimetypes in the ``Accept`` HTTP request header. If this
value is specified, it must be in one of the following
forms: a mimetype match token in the form ``text/plain``, a
wildcard mimetype match token in the form ``text/*`` or a
match-all wildcard mimetype match token in the form ``*/*``.
If any of the forms matches the ``Accept`` header of the
request, this predicate will be true. If this predicate
returns ``False``, route matching continues.
effective_principals
If specified, this value should be a :term:`principal` identifier or
a sequence of principal identifiers. If the
:func:`pyramid.security.effective_principals` method indicates that
every principal named in the argument list is present in the current
request, this predicate will return True; otherwise it will return
False. For example:
``effective_principals=pyramid.security.Authenticated`` or
``effective_principals=('fred', 'group:admins')``.
.. versionadded:: 1.4a4
custom_predicates
This value should be a sequence of references to custom
predicate callables. Use custom predicates when no set of
predefined predicates does what you need. Custom predicates
can be combined with predefined predicates as necessary.
Each custom predicate callable should accept two arguments:
``info`` and ``request`` and should return either ``True``
or ``False`` after doing arbitrary evaluation of the info
and/or the request. If all custom and non-custom predicate
callables return ``True`` the associated route will be
considered viable for a given request. If any predicate
callable returns ``False``, route matching continues. Note
that the value ``info`` passed to a custom route predicate
is a dictionary containing matching information; see
:ref:`custom_route_predicates` for more information about
``info``.
predicates
Pass a key/value pair here to use a third-party predicate
registered via
:meth:`pyramid.config.Configurator.add_view_predicate`. More than
one key/value pair can be used at the same time. See
:ref:`view_and_route_predicates` for more information about
third-party predicates. This argument is new as of Pyramid 1.4.
View-Related Arguments
.. warning::
The arguments described below have been deprecated as of
:app:`Pyramid` 1.1. *Do not use these for new development; they
should only be used to support older code bases which depend upon
them.* Use a separate call to
:meth:`pyramid.config.Configurator.add_view` to associate a view
with a route using the ``route_name`` argument.
view
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
A Python object or :term:`dotted Python name` to the same
object that will be used as a view callable when this route
matches. e.g. ``mypackage.views.my_view``.
view_context
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
A class or an :term:`interface` or :term:`dotted Python
name` to the same object which the :term:`context` of the
view should match for the view named by the route to be
used. This argument is only useful if the ``view``
attribute is used. If this attribute is not specified, the
default (``None``) will be used.
If the ``view`` argument is not provided, this argument has
no effect.
This attribute can also be spelled as ``for_`` or ``view_for``.
view_permission
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
The permission name required to invoke the view associated
with this route. e.g. ``edit``. (see
:ref:`using_security_with_urldispatch` for more information
about permissions).
If the ``view`` attribute is not provided, this argument has
no effect.
This argument can also be spelled as ``permission``.
view_renderer
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
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``). 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 implementation will be used to construct a
response from the view return value. If the renderer term
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 response
from the view return value. See
:ref:`views_which_use_a_renderer` for more information.
If the ``view`` argument is not provided, this argument has
no effect.
This argument can also be spelled as ``renderer``.
view_attr
.. warning:: Deprecated as of :app:`Pyramid` 1.1.
The view machinery defaults to using the ``__call__`` method
of the view callable (or the function itself, if the view
callable is a function) to obtain a response dictionary.
The ``attr`` value allows you to vary the method attribute
used to obtain the response. For example, if your view was
a class, and the class has a method named ``index`` and you
wanted to use this method instead of the class' ``__call__``
method to return the response, you'd say ``attr="index"`` in
the view configuration for the view. This is
most useful when the view definition is a class.
If the ``view`` argument is not provided, this argument has no
effect.
"""
# these are route predicates; if they do not match, the next route
# in the routelist will be tried
if request_method is not None:
request_method = as_sorted_tuple(request_method)
factory = self.maybe_dotted(factory)
if pattern is None:
pattern = path
if pattern is None:
raise ConfigurationError('"pattern" argument may not be None')
if self.route_prefix:
pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
mapper = self.get_routes_mapper()
introspectables = []
intr = self.introspectable('routes',
name,
'%s (pattern: %r)' % (name, pattern),
'route')
intr['name'] = name
intr['pattern'] = pattern
intr['factory'] = factory
intr['xhr'] = xhr
intr['request_methods'] = request_method
intr['path_info'] = path_info
intr['request_param'] = request_param
intr['header'] = header
intr['accept'] = accept
intr['traverse'] = traverse
intr['custom_predicates'] = custom_predicates
intr['pregenerator'] = pregenerator
intr['static'] = static
intr['use_global_views'] = use_global_views
introspectables.append(intr)
if factory:
factory_intr = self.introspectable('root factories',
name,
self.object_description(factory),
'root factory')
factory_intr['factory'] = factory
factory_intr['route_name'] = name
factory_intr.relate('routes', name)
introspectables.append(factory_intr)
def register_route_request_iface():
request_iface = self.registry.queryUtility(IRouteRequest, name=name)
if request_iface is None:
if use_global_views:
bases = (IRequest,)
else:
bases = ()
request_iface = route_request_iface(name, bases)
self.registry.registerUtility(
request_iface, IRouteRequest, name=name)
def register_connect():
pvals = predicates.copy()
pvals.update(
dict(
xhr=xhr,
request_method=request_method,
path_info=path_info,
request_param=request_param,
header=header,
accept=accept,
traverse=traverse,
custom=predvalseq(custom_predicates),
)
)
predlist = self.get_predlist('route')
_, preds, _ = predlist.make(self, **pvals)
route = mapper.connect(
name, pattern, factory, predicates=preds,
pregenerator=pregenerator, static=static
)
intr['object'] = route
return route
# We have to connect routes in the order they were provided;
# we can't use a phase to do that, because when the actions are
# sorted, actions in the same phase lose relative ordering
self.action(('route-connect', name), register_connect)
# But IRouteRequest interfaces must be registered before we begin to
# process view registrations (in phase 3)
self.action(('route', name), register_route_request_iface,
order=PHASE2_CONFIG, introspectables=introspectables)
# deprecated adding views from add_route; must come after
# route registration for purposes of autocommit ordering
if any([view, view_context, view_permission, view_renderer,
view_for, for_, permission, renderer, view_attr]):
self._add_view_from_route(
route_name=name,
view=view,
permission=view_permission or permission,
context=view_context or view_for or for_,
renderer=view_renderer or renderer,
attr=view_attr,
)
@action_method
def add_route_predicate(self, name, factory, weighs_more_than=None,
weighs_less_than=None):
""" Adds a route predicate factory. The view predicate can later be
named as a keyword argument to
:meth:`pyramid.config.Configurator.add_route`.
``name`` should be the name of the predicate. It must be a valid
Python identifier (it will be used as a keyword argument to
``add_view``).
``factory`` should be a :term:`predicate factory`.
See :ref:`view_and_route_predicates` for more information.
.. note::
This method is new as of Pyramid 1.4.
"""
self._add_predicate(
'route',
name,
factory,
weighs_more_than=weighs_more_than,
weighs_less_than=weighs_less_than
)
def add_default_route_predicates(self):
p = pyramid.config.predicates
for (name, factory) in (
('xhr', p.XHRPredicate),
('request_method', p.RequestMethodPredicate),
('path_info', p.PathInfoPredicate),
('request_param', p.RequestParamPredicate),
('header', p.HeaderPredicate),
('accept', p.AcceptPredicate),
('effective_principals', p.EffectivePrincipalsPredicate),
('custom', p.CustomPredicate),
('traverse', p.TraversePredicate),
):
self.add_route_predicate(name, factory)
def get_routes_mapper(self):
""" Return the :term:`routes mapper` object associated with
this configurator's :term:`registry`."""
mapper = self.registry.queryUtility(IRoutesMapper)
if mapper is None:
mapper = RoutesMapper()
self.registry.registerUtility(mapper, IRoutesMapper)
return mapper
def _add_view_from_route(self,
route_name,
view,
context,
permission,
renderer,
attr,
):
if view:
self.add_view(
permission=permission,
context=context,
view=view,
name='',
route_name=route_name,
renderer=renderer,
attr=attr,
)
else:
# prevent mistakes due to misunderstanding of how hybrid calls to
# add_route and add_view interact
if attr:
raise ConfigurationError(
'view_attr argument not permitted without view '
'argument')
if context:
raise ConfigurationError(
'view_context argument not permitted without view '
'argument')
if permission:
raise ConfigurationError(
'view_permission argument not permitted without view '
'argument')
if renderer:
raise ConfigurationError(
'view_renderer argument not permitted without '
'view argument')
warnings.warn(
'Passing view-related arguments to add_route() is deprecated as of '
'Pyramid 1.1. Use add_view() to associate a view with a route '
'instead. See "Deprecations" in "What\'s New in Pyramid 1.1" '
'within the general Pyramid documentation for further details.',
DeprecationWarning,
4)
pyramid-1.4.5/pyramid/config/views.py 0000664 0001750 0001750 00000235240 12210154276 017111 0 ustar takaki takaki import inspect
import operator
import os
from zope.interface import (
Interface,
implementedBy,
implementer,
provider,
)
from zope.interface.interfaces import IInterface
from pyramid.interfaces import (
IAuthenticationPolicy,
IAuthorizationPolicy,
IDebugLogger,
IDefaultPermission,
IException,
IExceptionViewClassifier,
IMultiView,
IRendererFactory,
IRequest,
IResponse,
IRouteRequest,
ISecuredView,
IStaticURLInfo,
IView,
IViewClassifier,
IViewMapper,
IViewMapperFactory,
PHASE1_CONFIG,
)
from pyramid import renderers
from pyramid.compat import (
string_types,
urlparse,
url_quote,
WIN,
is_bound_method,
is_nonstr_iter
)
from pyramid.exceptions import (
ConfigurationError,
PredicateMismatch,
)
from pyramid.httpexceptions import (
HTTPForbidden,
HTTPNotFound,
)
from pyramid.registry import (
predvalseq,
Deferred,
)
from pyramid.response import Response
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.static import static_view
from pyramid.threadlocal import get_current_registry
from pyramid.view import (
render_view_to_response,
AppendSlashNotFoundViewFactory,
)
from pyramid.util import (
object_description,
viewdefaults,
action_method,
)
import pyramid.config.predicates
from pyramid.config.util import (
DEFAULT_PHASH,
MAX_ORDER,
takes_one_arg,
)
urljoin = urlparse.urljoin
url_parse = urlparse.urlparse
def view_description(view):
try:
return view.__text__
except AttributeError:
# custom view mappers might not add __text__
return object_description(view)
def wraps_view(wrapper):
def inner(self, view):
wrapper_view = wrapper(self, view)
return preserve_view_attrs(view, wrapper_view)
return inner
def preserve_view_attrs(view, wrapper):
if view is None:
return wrapper
if wrapper is view:
return view
original_view = getattr(view, '__original_view__', None)
if original_view is None:
original_view = view
wrapper.__wraps__ = view
wrapper.__original_view__ = original_view
wrapper.__module__ = view.__module__
wrapper.__doc__ = view.__doc__
try:
wrapper.__name__ = view.__name__
except AttributeError:
wrapper.__name__ = repr(view)
# attrs that may not exist on "view", but, if so, must be attached to
# "wrapped view"
for attr in ('__permitted__', '__call_permissive__', '__permission__',
'__predicated__', '__predicates__', '__accept__',
'__order__', '__text__'):
try:
setattr(wrapper, attr, getattr(view, attr))
except AttributeError:
pass
return wrapper
class ViewDeriver(object):
def __init__(self, **kw):
self.kw = kw
self.registry = kw['registry']
self.authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
self.authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
self.logger = self.registry.queryUtility(IDebugLogger)
def __call__(self, view):
return self.attr_wrapped_view(
self.predicated_view(
self.authdebug_view(
self.secured_view(
self.owrapped_view(
self.http_cached_view(
self.decorated_view(
self.rendered_view(
self.mapped_view(
view)))))))))
@wraps_view
def mapped_view(self, view):
mapper = self.kw.get('mapper')
if mapper is None:
mapper = getattr(view, '__view_mapper__', None)
if mapper is None:
mapper = self.registry.queryUtility(IViewMapperFactory)
if mapper is None:
mapper = DefaultViewMapper
mapped_view = mapper(**self.kw)(view)
return mapped_view
@wraps_view
def owrapped_view(self, view):
wrapper_viewname = self.kw.get('wrapper_viewname')
viewname = self.kw.get('viewname')
if not wrapper_viewname:
return view
def _owrapped_view(context, request):
response = view(context, request)
request.wrapped_response = response
request.wrapped_body = response.body
request.wrapped_view = view
wrapped_response = render_view_to_response(context, request,
wrapper_viewname)
if wrapped_response is None:
raise ValueError(
'No wrapper view named %r found when executing view '
'named %r' % (wrapper_viewname, viewname))
return wrapped_response
return _owrapped_view
@wraps_view
def http_cached_view(self, view):
if self.registry.settings.get('prevent_http_cache', False):
return view
seconds = self.kw.get('http_cache')
if seconds is None:
return view
options = {}
if isinstance(seconds, (tuple, list)):
try:
seconds, options = seconds
except ValueError:
raise ConfigurationError(
'If http_cache parameter is a tuple or list, it must be '
'in the form (seconds, options); not %s' % (seconds,))
def wrapper(context, request):
response = view(context, request)
prevent_caching = getattr(response.cache_control, 'prevent_auto',
False)
if not prevent_caching:
response.cache_expires(seconds, **options)
return response
return wrapper
@wraps_view
def secured_view(self, view):
permission = self.kw.get('permission')
if permission == NO_PERMISSION_REQUIRED:
# allow views registered within configurations that have a
# default permission to explicitly override the default
# permission, replacing it with no permission at all
permission = None
wrapped_view = view
if self.authn_policy and self.authz_policy and (permission is not None):
def _permitted(context, request):
principals = self.authn_policy.effective_principals(request)
return self.authz_policy.permits(context, principals,
permission)
def _secured_view(context, request):
result = _permitted(context, request)
if result:
return view(context, request)
view_name = getattr(view, '__name__', view)
msg = getattr(
request, 'authdebug_message',
'Unauthorized: %s failed permission check' % view_name)
raise HTTPForbidden(msg, result=result)
_secured_view.__call_permissive__ = view
_secured_view.__permitted__ = _permitted
_secured_view.__permission__ = permission
wrapped_view = _secured_view
return wrapped_view
@wraps_view
def authdebug_view(self, view):
wrapped_view = view
settings = self.registry.settings
permission = self.kw.get('permission')
if settings and settings.get('debug_authorization', False):
def _authdebug_view(context, request):
view_name = getattr(request, 'view_name', None)
if self.authn_policy and self.authz_policy:
if permission is NO_PERMISSION_REQUIRED:
msg = 'Allowed (NO_PERMISSION_REQUIRED)'
elif permission is None:
msg = 'Allowed (no permission registered)'
else:
principals = self.authn_policy.effective_principals(
request)
msg = str(self.authz_policy.permits(context, principals,
permission))
else:
msg = 'Allowed (no authorization policy in use)'
view_name = getattr(request, 'view_name', None)
url = getattr(request, 'url', None)
msg = ('debug_authorization of url %s (view name %r against '
'context %r): %s' % (url, view_name, context, msg))
self.logger and self.logger.debug(msg)
if request is not None:
request.authdebug_message = msg
return view(context, request)
wrapped_view = _authdebug_view
return wrapped_view
@wraps_view
def predicated_view(self, view):
preds = self.kw.get('predicates', ())
if not preds:
return view
def predicate_wrapper(context, request):
for predicate in preds:
if not predicate(context, request):
view_name = getattr(view, '__name__', view)
raise PredicateMismatch(
'predicate mismatch for view %s (%s)' % (
view_name, predicate.text()))
return view(context, request)
def checker(context, request):
return all((predicate(context, request) for predicate in
preds))
predicate_wrapper.__predicated__ = checker
predicate_wrapper.__predicates__ = preds
return predicate_wrapper
@wraps_view
def attr_wrapped_view(self, view):
kw = self.kw
accept, order, phash = (kw.get('accept', None),
kw.get('order', MAX_ORDER),
kw.get('phash', DEFAULT_PHASH))
# this is a little silly but we don't want to decorate the original
# function with attributes that indicate accept, order, and phash,
# so we use a wrapper
if (
(accept is None) and
(order == MAX_ORDER) and
(phash == DEFAULT_PHASH)
):
return view # defaults
def attr_view(context, request):
return view(context, request)
attr_view.__accept__ = accept
attr_view.__order__ = order
attr_view.__phash__ = phash
attr_view.__view_attr__ = self.kw.get('attr')
attr_view.__permission__ = self.kw.get('permission')
return attr_view
@wraps_view
def rendered_view(self, view):
# one way or another this wrapper must produce a Response (unless
# the renderer is a NullRendererHelper)
renderer = self.kw.get('renderer')
if renderer is None:
# register a default renderer if you want super-dynamic
# rendering. registering a default renderer will also allow
# override_renderer to work if a renderer is left unspecified for
# a view registration.
return self._response_resolved_view(view)
if renderer is renderers.null_renderer:
return view
return self._rendered_view(view, renderer)
def _rendered_view(self, view, view_renderer):
def rendered_view(context, request):
renderer = view_renderer
result = view(context, request)
if result.__class__ is Response: # potential common case
response = result
else:
registry = self.registry
# this must adapt, it can't do a simple interface check
# (avoid trying to render webob responses)
response = registry.queryAdapterOrSelf(result, IResponse)
if response is None:
attrs = getattr(request, '__dict__', {})
if 'override_renderer' in attrs:
# renderer overridden by newrequest event or other
renderer_name = attrs.pop('override_renderer')
renderer = renderers.RendererHelper(
name=renderer_name,
package=self.kw.get('package'),
registry = registry)
if '__view__' in attrs:
view_inst = attrs.pop('__view__')
else:
view_inst = getattr(view, '__original_view__', view)
response = renderer.render_view(request, result, view_inst,
context)
return response
return rendered_view
def _response_resolved_view(self, view):
registry = self.registry
def viewresult_to_response(context, request):
result = view(context, request)
if result.__class__ is Response: # common case
response = result
else:
response = registry.queryAdapterOrSelf(result, IResponse)
if response is None:
if result is None:
append = (' You may have forgotten to return a value '
'from the view callable.')
elif isinstance(result, dict):
append = (' You may have forgotten to define a '
'renderer in the view configuration.')
else:
append = ''
msg = ('Could not convert return value of the view '
'callable %s into a response object. '
'The value returned was %r.' + append)
raise ValueError(msg % (view_description(view), result))
return response
return viewresult_to_response
@wraps_view
def decorated_view(self, view):
decorator = self.kw.get('decorator')
if decorator is None:
return view
return decorator(view)
@implementer(IViewMapper)
@provider(IViewMapperFactory)
class DefaultViewMapper(object):
def __init__(self, **kw):
self.attr = kw.get('attr')
def __call__(self, view):
if inspect.isclass(view):
view = self.map_class(view)
else:
view = self.map_nonclass(view)
return view
def map_class(self, view):
ronly = requestonly(view, self.attr)
if ronly:
mapped_view = self.map_class_requestonly(view)
else:
mapped_view = self.map_class_native(view)
mapped_view.__text__ = 'method %s of %s' % (
self.attr or '__call__', object_description(view))
return mapped_view
def map_nonclass(self, view):
# We do more work here than appears necessary to avoid wrapping the
# view unless it actually requires wrapping (to avoid function call
# overhead).
mapped_view = view
ronly = requestonly(view, self.attr)
if ronly:
mapped_view = self.map_nonclass_requestonly(view)
elif self.attr:
mapped_view = self.map_nonclass_attr(view)
if inspect.isroutine(mapped_view):
# This branch will be true if the view is a function or a method.
# We potentially mutate an unwrapped object here if it's a
# function. We do this to avoid function call overhead of
# injecting another wrapper. However, we must wrap if the
# function is a bound method because we can't set attributes on a
# bound method.
if is_bound_method(view):
_mapped_view = mapped_view
def mapped_view(context, request):
return _mapped_view(context, request)
if self.attr is not None:
mapped_view.__text__ = 'attr %s of %s' % (
self.attr, object_description(view))
else:
mapped_view.__text__ = object_description(view)
return mapped_view
def map_class_requestonly(self, view):
# its a class that has an __init__ which only accepts request
attr = self.attr
def _class_requestonly_view(context, request):
inst = view(request)
request.__view__ = inst
if attr is None:
response = inst()
else:
response = getattr(inst, attr)()
return response
return _class_requestonly_view
def map_class_native(self, view):
# its a class that has an __init__ which accepts both context and
# request
attr = self.attr
def _class_view(context, request):
inst = view(context, request)
request.__view__ = inst
if attr is None:
response = inst()
else:
response = getattr(inst, attr)()
return response
return _class_view
def map_nonclass_requestonly(self, view):
# its a function that has a __call__ which accepts only a single
# request argument
attr = self.attr
def _requestonly_view(context, request):
if attr is None:
response = view(request)
else:
response = getattr(view, attr)(request)
return response
return _requestonly_view
def map_nonclass_attr(self, view):
# its a function that has a __call__ which accepts both context and
# request, but still has an attr
def _attr_view(context, request):
response = getattr(view, self.attr)(context, request)
return response
return _attr_view
def requestonly(view, attr=None):
return takes_one_arg(view, attr=attr, argname='request')
@implementer(IMultiView)
class MultiView(object):
def __init__(self, name):
self.name = name
self.media_views = {}
self.views = []
self.accepts = []
def __discriminator__(self, context, request):
# used by introspection systems like so:
# view = adapters.lookup(....)
# view.__discriminator__(context, request) -> view's discriminator
# so that superdynamic systems can feed the discriminator to
# the introspection system to get info about it
view = self.match(context, request)
return view.__discriminator__(context, request)
def add(self, view, order, accept=None, phash=None):
if phash is not None:
for i, (s, v, h) in enumerate(list(self.views)):
if phash == h:
self.views[i] = (order, view, phash)
return
if accept is None or '*' in accept:
self.views.append((order, view, phash))
self.views.sort(key=operator.itemgetter(0))
else:
subset = self.media_views.setdefault(accept, [])
for i, (s, v, h) in enumerate(list(subset)):
if phash == h:
subset[i] = (order, view, phash)
return
else:
subset.append((order, view, phash))
subset.sort(key=operator.itemgetter(0))
accepts = set(self.accepts)
accepts.add(accept)
self.accepts = list(accepts) # dedupe
def get_views(self, request):
if self.accepts and hasattr(request, 'accept'):
accepts = self.accepts[:]
views = []
while accepts:
match = request.accept.best_match(accepts)
if match is None:
break
subset = self.media_views[match]
views.extend(subset)
accepts.remove(match)
views.extend(self.views)
return views
return self.views
def match(self, context, request):
for order, view, phash in self.get_views(request):
if not hasattr(view, '__predicated__'):
return view
if view.__predicated__(context, request):
return view
raise PredicateMismatch(self.name)
def __permitted__(self, context, request):
view = self.match(context, request)
if hasattr(view, '__permitted__'):
return view.__permitted__(context, request)
return True
def __call_permissive__(self, context, request):
view = self.match(context, request)
view = getattr(view, '__call_permissive__', view)
return view(context, request)
def __call__(self, context, request):
for order, view, phash in self.get_views(request):
try:
return view(context, request)
except PredicateMismatch:
continue
raise PredicateMismatch(self.name)
class ViewsConfiguratorMixin(object):
@viewdefaults
@action_method
def add_view(
self,
view=None,
name="",
for_=None,
permission=None,
request_type=None,
route_name=None,
request_method=None,
request_param=None,
containment=None,
attr=None,
renderer=None,
wrapper=None,
xhr=None,
accept=None,
header=None,
path_info=None,
custom_predicates=(),
context=None,
decorator=None,
mapper=None,
http_cache=None,
match_param=None,
check_csrf=None,
**predicates):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
arguments. Predicate arguments narrow the circumstances in
which the view callable will be invoked when a request is
presented to :app:`Pyramid`; non-predicate arguments are
informational.
Non-Predicate Arguments
view
A :term:`view callable` or a :term:`dotted Python name`
which refers to a view callable. This argument is required
unless a ``renderer`` argument also exists. If a
``renderer`` argument is passed, and a ``view`` argument is
not provided, the view callable defaults to a callable that
returns an empty dictionary (see
:ref:`views_which_use_a_renderer`).
permission
A :term:`permission` that the user must possess in order to invoke
the :term:`view callable`. See :ref:`view_security_section` for
more information about view security and permissions. This is
often a string like ``view`` or ``edit``.
If ``permission`` is omitted, a *default* permission may be used
for this view registration if one was named as the
:class:`pyramid.config.Configurator` constructor's
``default_permission`` argument, or if
:meth:`pyramid.config.Configurator.set_default_permission` was used
prior to this view registration. Pass the value
:data:`pyramid.security.NO_PERMISSION_REQUIRED` as the permission
argument to explicitly indicate that the view should always be
executable by entirely anonymous users, regardless of the default
permission, bypassing any :term:`authorization policy` that may be
in effect.
attr
This knob is most useful when the view definition is a class.
The view machinery defaults to using the ``__call__`` method
of the :term:`view callable` (or the function itself, if the
view callable is a function) to obtain a response. The
``attr`` value allows you to vary the method attribute used
to obtain the response. For example, if your view was a
class, and the class has a method named ``index`` and you
wanted to use this method instead of the class' ``__call__``
method to return the response, you'd say ``attr="index"`` in the
view configuration for the view.
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.
Note that if the view itself returns a :term:`response` (see
:ref:`the_response`), the specified renderer implementation
is never called.
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` 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:`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 is passed back to the upstream
:app:`Pyramid` machinery unmodified).
http_cache
.. note:: This feature is new as of Pyramid 1.1.
When you supply an ``http_cache`` value to a view configuration,
the ``Expires`` and ``Cache-Control`` headers of a response
generated by the associated view callable are modified. The value
for ``http_cache`` may be one of the following:
- A nonzero integer. If it's a nonzero integer, it's treated as a
number of seconds. This number of seconds will be used to
compute the ``Expires`` header and the ``Cache-Control:
max-age`` parameter of responses to requests which call this view.
For example: ``http_cache=3600`` instructs the requesting browser
to 'cache this response for an hour, please'.
- A ``datetime.timedelta`` instance. If it's a
``datetime.timedelta`` instance, it will be converted into a
number of seconds, and that number of seconds will be used to
compute the ``Expires`` header and the ``Cache-Control:
max-age`` parameter of responses to requests which call this view.
For example: ``http_cache=datetime.timedelta(days=1)`` instructs
the requesting browser to 'cache this response for a day, please'.
- Zero (``0``). If the value is zero, the ``Cache-Control`` and
``Expires`` headers present in all responses from this view will
be composed such that client browser cache (and any intermediate
caches) are instructed to never cache the response.
- A two-tuple. If it's a two tuple (e.g. ``http_cache=(1,
{'public':True})``), the first value in the tuple may be a
nonzero integer or a ``datetime.timedelta`` instance; in either
case this value will be used as the number of seconds to cache
the response. The second value in the tuple must be a
dictionary. The values present in the dictionary will be used as
input to the ``Cache-Control`` response header. For example:
``http_cache=(3600, {'public':True})`` means 'cache for an hour,
and add ``public`` to the Cache-Control header of the response'.
All keys and values supported by the
``webob.cachecontrol.CacheControl`` interface may be added to the
dictionary. Supplying ``{'public':True}`` is equivalent to
calling ``response.cache_control.public = True``.
Providing a non-tuple value as ``http_cache`` is equivalent to
calling ``response.cache_expires(value)`` within your view's body.
Providing a two-tuple value as ``http_cache`` is equivalent to
calling ``response.cache_expires(value[0], **value[1])`` within your
view's body.
If you wish to avoid influencing, the ``Expires`` header, and
instead wish to only influence ``Cache-Control`` headers, pass a
tuple as ``http_cache`` with the first element of ``None``, e.g.:
``(None, {'public':True})``.
If you wish to prevent a view that uses ``http_cache`` in its
configuration from having its caching response headers changed by
this machinery, set ``response.cache_control.prevent_auto = True``
before returning the response from the view. This effectively
disables any HTTP caching done by ``http_cache`` for that response.
wrapper
The :term:`view name` of a different :term:`view
configuration` which will receive the response body of this
view as the ``request.wrapped_body`` attribute of its own
:term:`request`, and the :term:`response` returned by this
view as the ``request.wrapped_response`` attribute of its
own request. Using a wrapper makes it possible to "chain"
views together to form a composite response. The response
of the outermost wrapper view will be returned to the user.
The wrapper view will be found as any view is found: see
:ref:`view_lookup`. The "best" wrapper view will be found
based on the lookup ordering: "under the hood" this wrapper
view is looked up via
``pyramid.view.render_view_to_response(context, request,
'wrapper_viewname')``. The context and request of a wrapper
view is the same context and request of the inner view. If
this attribute is unspecified, no view wrapping is done.
decorator
A :term:`dotted Python name` to function (or the function itself,
or an iterable of the aforementioned) which will be used to
decorate the registered :term:`view callable`. The decorator
function(s) will be called with the view callable as a single
argument. The view callable it is passed will accept
``(context, request)``. The decorator(s) must return a
replacement view callable which also accepts ``(context,
request)``.
If decorator is an iterable, the callables will be combined and
used in the order provided as a decorator.
For example::
@view_config(...,
decorator=(decorator2,
decorator1))
def myview(request):
....
Is similar to doing::
@view_config(...)
@decorator2
@decorator1
def myview(request):
...
Except with the existing benefits of ``decorator=`` (having a common
decorator syntax for all view calling conventions and not having to
think about preserving function attributes such as ``__name__`` and
``__module__`` within decorator logic).
Passing an iterable is only supported as of :app:`Pyramid` 1.4a4.
mapper
A Python object or :term:`dotted Python name` which refers to a
:term:`view mapper`, or ``None``. By default it is ``None``, which
indicates that the view should use the default view mapper. This
plug-point is useful for Pyramid extension developers, but it's not
very useful for 'civilians' who are just developing stock Pyramid
applications. Pay no attention to the man behind the curtain.
Predicate Arguments
name
The :term:`view name`. Read :ref:`traversal_chapter` to
understand the concept of a view name.
context
An object or a :term:`dotted Python name` referring to an
interface or class object 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. This argument may also be provided
to ``add_view`` as ``for_`` (an older, still-supported
spelling).
route_name
This value must match the ``name`` of a :term:`route
configuration` declaration (see :ref:`urldispatch_chapter`)
that must match before this view will be called.
request_type
This value should be an :term:`interface` that the
:term:`request` must provide in order for this view to be
found and called. This value exists only for backwards
compatibility purposes.
request_method
This value can be one of the strings ``GET``, ``POST``, ``PUT``,
``DELETE``, or ``HEAD`` representing an HTTP ``REQUEST_METHOD``, or
a tuple containing one or more of these strings. A view
declaration with this argument ensures that the view will only be
called when the request's ``method`` attribute (aka the
``REQUEST_METHOD`` of the WSGI environment) string matches a
supplied value. Note that use of ``GET`` also implies that the
view will respond to ``HEAD`` as of Pyramid 1.4.
.. note:: The ability to pass a tuple of items as
``request_method`` is new as of Pyramid 1.2. Previous
versions allowed only a string.
request_param
This value can be any string or any sequence of strings. A view
declaration with this argument ensures that the view will only be
called when the :term:`request` has a key in the ``request.params``
dictionary (an HTTP ``GET`` or ``POST`` variable) that has a
name which matches the supplied value (if the value is a string)
or values (if the value is a tuple). If any value
supplied has a ``=`` sign in it,
e.g. ``request_param="foo=123"``, then the key (``foo``)
must both exist in the ``request.params`` dictionary, *and*
the value must match the right hand side of the expression
(``123``) for the view to "match" the current request.
match_param
.. note:: This feature is new as of :app:`Pyramid` 1.2.
This value can be a string of the format "key=value" or a tuple
containing one or more of these strings.
A view declaration with this argument ensures that the view will
only be called when the :term:`request` has key/value pairs in its
:term:`matchdict` that equal those supplied in the predicate.
e.g. ``match_param="action=edit" would require the ``action``
parameter in the :term:`matchdict` match the right hand side of
the expression (``edit``) for the view to "match" the current
request.
If the ``match_param`` is a tuple, every key/value pair must match
for the predicate to pass.
containment
This value should be a Python class or :term:`interface` (or a
:term:`dotted Python name`) that an object in the
:term:`lineage` of the context must provide in order for this view
to be found and called. The nodes in your object graph must be
"location-aware" to use this feature. See
:ref:`location_aware` for more information about
location-awareness.
xhr
This value should be either ``True`` or ``False``. If this
value is specified and is ``True``, the :term:`request`
must possess an ``HTTP_X_REQUESTED_WITH`` (aka
``X-Requested-With``) header that has the value
``XMLHttpRequest`` for this view to be found and called.
This is useful for detecting AJAX requests issued from
jQuery, Prototype and other Javascript libraries.
accept
The value of this argument represents a match query for one
or more mimetypes in the ``Accept`` HTTP request header. If
this value is specified, it must be in one of the following
forms: a mimetype match token in the form ``text/plain``, a
wildcard mimetype match token in the form ``text/*`` or a
match-all wildcard mimetype match token in the form ``*/*``.
If any of the forms matches the ``Accept`` header of the
request, this predicate will be true.
header
This value represents an HTTP header name or a header
name/value pair. If the value contains a ``:`` (colon), it
will be considered a name/value pair
(e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The
value portion should be a regular expression. If the value
does not contain a colon, the entire value will be
considered to be the header name
(e.g. ``If-Modified-Since``). If the value evaluates to a
header name only without a value, the header specified by
the name must be present in the request for this predicate
to be true. If the value evaluates to a header name/value
pair, the header specified by the name must be present in
the request *and* the regular expression specified as the
value must match the header value. Whether or not the value
represents a header name or a header name/value pair, the
case of the header name is not significant.
path_info
This value represents a regular expression pattern that will
be tested against the ``PATH_INFO`` WSGI environment
variable. If the regex matches, this predicate will be
``True``.
check_csrf
If specified, this value should be one of ``None``, ``True``,
``False``, or a string representing the 'check name'. If the value
is ``True`` or a string, CSRF checking will be performed. If the
value is ``False`` or ``None``, CSRF checking will not be performed.
If the value provided is a string, that string will be used as the
'check name'. If the value provided is ``True``, ``csrf_token`` will
be used as the check name.
If CSRF checking is performed, the checked value will be the value
of ``request.params[check_name]``. This value will be compared
against the value of ``request.session.get_csrf_token()``, and the
check will pass if these two values are the same. If the check
passes, the associated view will be permitted to execute. If the
check fails, the associated view will not be permitted to execute.
Note that using this feature requires a :term:`session factory` to
have been configured.
.. versionadded:: 1.4a2
physical_path
If specified, this value should be a string or a tuple representing
the :term:`physical path` of the context found via traversal for this
predicate to match as true. For example: ``physical_path='/'`` or
``physical_path='/a/b/c'`` or ``physical_path=('', 'a', 'b', 'c')``.
This is not a path prefix match or a regex, it's a whole-path match.
It's useful when you want to always potentially show a view when some
object is traversed to, but you can't be sure about what kind of
object it will be, so you can't use the ``context`` predicate. The
individual path elements inbetween slash characters or in tuple
elements should be the Unicode representation of the name of the
resource and should not be encoded in any way.
.. versionadded:: 1.4a3
effective_principals
If specified, this value should be a :term:`principal` identifier or
a sequence of principal identifiers. If the
:func:`pyramid.security.effective_principals` method indicates that
every principal named in the argument list is present in the current
request, this predicate will return True; otherwise it will return
False. For example:
``effective_principals=pyramid.security.Authenticated`` or
``effective_principals=('fred', 'group:admins')``.
.. versionadded:: 1.4a4
custom_predicates
This value should be a sequence of references to custom predicate
callables. Use custom predicates when no set of predefined
predicates do what you need. Custom predicates can be combined with
predefined predicates as necessary. Each custom predicate callable
should accept two arguments: ``context`` and ``request`` and should
return either ``True`` or ``False`` after doing arbitrary evaluation
of the context and/or the request. The ``predicates`` argument to
this method and the ability to register third-party view predicates
via :meth:`pyramid.config.Configurator.add_view_predicate` obsoletes
this argument, but it is kept around for backwards compatibility.
predicates
Pass a key/value pair here to use a third-party predicate
registered via
:meth:`pyramid.config.Configurator.add_view_predicate`. More than
one key/value pair can be used at the same time. See
:ref:`view_and_route_predicates` for more information about
third-party predicates.
.. versionadded: 1.4a1
"""
view = self.maybe_dotted(view)
context = self.maybe_dotted(context)
for_ = self.maybe_dotted(for_)
containment = self.maybe_dotted(containment)
mapper = self.maybe_dotted(mapper)
def combine(*decorators):
def decorated(view_callable):
# reversed() is allows a more natural ordering in the api
for decorator in reversed(decorators):
view_callable = decorator(view_callable)
return view_callable
return decorated
if is_nonstr_iter(decorator):
decorator = combine(*map(self.maybe_dotted, decorator))
else:
decorator = self.maybe_dotted(decorator)
if not view:
if renderer:
def view(context, request):
return {}
else:
raise ConfigurationError('"view" was not specified and '
'no "renderer" specified')
if request_type is not None:
request_type = self.maybe_dotted(request_type)
if not IInterface.providedBy(request_type):
raise ConfigurationError(
'request_type must be an interface, not %s' % request_type)
if context is None:
context = for_
r_context = context
if r_context is None:
r_context = Interface
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
registry = self.registry)
if accept is not None:
accept = accept.lower()
introspectables = []
pvals = predicates.copy()
pvals.update(
dict(
xhr=xhr,
request_method=request_method,
path_info=path_info,
request_param=request_param,
header=header,
accept=accept,
containment=containment,
request_type=request_type,
match_param=match_param,
check_csrf=check_csrf,
custom=predvalseq(custom_predicates),
)
)
def discrim_func():
# We need to defer the discriminator until we know what the phash
# is. It can't be computed any sooner because thirdparty
# predicates may not yet exist when add_view is called.
order, preds, phash = predlist.make(self, **pvals)
view_intr.update({'phash':phash, 'order':order, 'predicates':preds})
return ('view', context, name, route_name, phash)
discriminator = Deferred(discrim_func)
if inspect.isclass(view) and attr:
view_desc = 'method %r of %s' % (
attr, self.object_description(view))
else:
view_desc = self.object_description(view)
view_intr = self.introspectable('views',
discriminator,
view_desc,
'view')
view_intr.update(
dict(name=name,
context=context,
containment=containment,
request_param=request_param,
request_methods=request_method,
route_name=route_name,
attr=attr,
xhr=xhr,
accept=accept,
header=header,
path_info=path_info,
match_param=match_param,
check_csrf=check_csrf,
callable=view,
mapper=mapper,
decorator=decorator,
)
)
view_intr.update(**predicates)
introspectables.append(view_intr)
predlist = self.get_predlist('view')
def register(permission=permission, renderer=renderer):
# the discrim_func above is guaranteed to have been called already
order = view_intr['order']
preds = view_intr['predicates']
phash = view_intr['phash']
request_iface = IRequest
if route_name is not None:
request_iface = self.registry.queryUtility(IRouteRequest,
name=route_name)
if request_iface is None:
# route configuration should have already happened in
# phase 2
raise ConfigurationError(
'No route named %s found for view registration' %
route_name)
if renderer is None:
# use default renderer if one exists (reg'd in phase 1)
if self.registry.queryUtility(IRendererFactory) is not None:
renderer = renderers.RendererHelper(
name=None,
package=self.package,
registry=self.registry)
if permission is None:
# intent: will be None if no default permission is registered
# (reg'd in phase 1)
permission = self.registry.queryUtility(IDefaultPermission)
# added by discrim_func above during conflict resolving
preds = view_intr['predicates']
order = view_intr['order']
phash = view_intr['phash']
# __no_permission_required__ handled by _secure_view
deriver = ViewDeriver(
registry=self.registry,
permission=permission,
predicates=preds,
attr=attr,
renderer=renderer,
wrapper_viewname=wrapper,
viewname=name,
accept=accept,
order=order,
phash=phash,
package=self.package,
mapper=mapper,
decorator=decorator,
http_cache=http_cache,
)
derived_view = deriver(view)
derived_view.__discriminator__ = lambda *arg: discriminator
# __discriminator__ is used by superdynamic systems
# that require it for introspection after manual view lookup;
# see also MultiView.__discriminator__
view_intr['derived_callable'] = derived_view
registered = self.registry.adapters.registered
# A multiviews is a set of views which are registered for
# exactly the same context type/request type/name triad. Each
# consituent view in a multiview differs only by the
# predicates which it possesses.
# To find a previously registered view for a context
# type/request type/name triad, we need to use the
# ``registered`` method of the adapter registry rather than
# ``lookup``. ``registered`` ignores interface inheritance
# for the required and provided arguments, returning only a
# view registered previously with the *exact* triad we pass
# in.
# We need to do this three times, because we use three
# different interfaces as the ``provided`` interface while
# doing registrations, and ``registered`` performs exact
# matches on all the arguments it receives.
old_view = None
for view_type in (IView, ISecuredView, IMultiView):
old_view = registered((IViewClassifier, request_iface,
r_context), view_type, name)
if old_view is not None:
break
isexc = isexception(context)
def regclosure():
if hasattr(derived_view, '__call_permissive__'):
view_iface = ISecuredView
else:
view_iface = IView
self.registry.registerAdapter(
derived_view,
(IViewClassifier, request_iface, context), view_iface, name
)
if isexc:
self.registry.registerAdapter(
derived_view,
(IExceptionViewClassifier, request_iface, context),
view_iface, name)
is_multiview = IMultiView.providedBy(old_view)
old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH)
if old_view is None:
# - No component was yet registered for any of our I*View
# interfaces exactly; this is the first view for this
# triad.
regclosure()
elif (not is_multiview) and (old_phash == phash):
# - A single view component was previously registered with
# the same predicate hash as this view; this registration
# is therefore an override.
regclosure()
else:
# - A view or multiview was already registered for this
# triad, and the new view is not an override.
# XXX we could try to be more efficient here and register
# a non-secured view for a multiview if none of the
# multiview's consituent views have a permission
# associated with them, but this code is getting pretty
# rough already
if is_multiview:
multiview = old_view
else:
multiview = MultiView(name)
old_accept = getattr(old_view, '__accept__', None)
old_order = getattr(old_view, '__order__', MAX_ORDER)
multiview.add(old_view, old_order, old_accept, old_phash)
multiview.add(derived_view, order, accept, phash)
for view_type in (IView, ISecuredView):
# unregister any existing views
self.registry.adapters.unregister(
(IViewClassifier, request_iface, r_context),
view_type, name=name)
if isexc:
self.registry.adapters.unregister(
(IExceptionViewClassifier, request_iface,
r_context), view_type, name=name)
self.registry.registerAdapter(
multiview,
(IViewClassifier, request_iface, context),
IMultiView, name=name)
if isexc:
self.registry.registerAdapter(
multiview,
(IExceptionViewClassifier, request_iface, context),
IMultiView, name=name)
if mapper:
mapper_intr = self.introspectable(
'view mappers',
discriminator,
'view mapper for %s' % view_desc,
'view mapper'
)
mapper_intr['mapper'] = mapper
mapper_intr.relate('views', discriminator)
introspectables.append(mapper_intr)
if route_name:
view_intr.relate('routes', route_name) # see add_route
if renderer is not None and renderer.name and '.' in renderer.name:
# the renderer is a template
tmpl_intr = self.introspectable(
'templates',
discriminator,
renderer.name,
'template'
)
tmpl_intr.relate('views', discriminator)
tmpl_intr['name'] = renderer.name
tmpl_intr['type'] = renderer.type
tmpl_intr['renderer'] = renderer
tmpl_intr.relate('renderer factories', renderer.type)
introspectables.append(tmpl_intr)
if permission is not None:
# if a permission exists, register a permission introspectable
perm_intr = self.introspectable(
'permissions',
permission,
permission,
'permission'
)
perm_intr['value'] = permission
perm_intr.relate('views', discriminator)
introspectables.append(perm_intr)
self.action(discriminator, register, introspectables=introspectables)
@action_method
def add_view_predicate(self, name, factory, weighs_more_than=None,
weighs_less_than=None):
""" Adds a view predicate factory. The associated view predicate can
later be named as a keyword argument to
:meth:`pyramid.config.Configurator.add_view` in the
``predicates`` anonyous keyword argument dictionary.
``name`` should be the name of the predicate. It must be a valid
Python identifier (it will be used as a keyword argument to
``add_view`` by others).
``factory`` should be a :term:`predicate factory`.
See :ref:`view_and_route_predicates` for more information.
.. note::
This method is new as of Pyramid 1.4.
"""
self._add_predicate(
'view',
name,
factory,
weighs_more_than=weighs_more_than,
weighs_less_than=weighs_less_than
)
def add_default_view_predicates(self):
p = pyramid.config.predicates
for (name, factory) in (
('xhr', p.XHRPredicate),
('request_method', p.RequestMethodPredicate),
('path_info', p.PathInfoPredicate),
('request_param', p.RequestParamPredicate),
('header', p.HeaderPredicate),
('accept', p.AcceptPredicate),
('containment', p.ContainmentPredicate),
('request_type', p.RequestTypePredicate),
('match_param', p.MatchParamPredicate),
('check_csrf', p.CheckCSRFTokenPredicate),
('physical_path', p.PhysicalPathPredicate),
('effective_principals', p.EffectivePrincipalsPredicate),
('custom', p.CustomPredicate),
):
self.add_view_predicate(name, factory)
def derive_view(self, view, attr=None, renderer=None):
"""
Create a :term:`view callable` using the function, instance,
or class (or :term:`dotted Python name` referring to the same)
provided as ``view`` object.
.. warning::
This method is typically only used by :app:`Pyramid` framework
extension authors, not by :app:`Pyramid` application developers.
This is API is useful to framework extenders who create
pluggable systems which need to register 'proxy' view
callables for functions, instances, or classes which meet the
requirements of being a :app:`Pyramid` view callable. For
example, a ``some_other_framework`` function in another
framework may want to allow a user to supply a view callable,
but he may want to wrap the view callable in his own before
registering the wrapper as a :app:`Pyramid` view callable.
Because a :app:`Pyramid` view callable can be any of a
number of valid objects, the framework extender will not know
how to call the user-supplied object. Running it through
``derive_view`` normalizes it to a callable which accepts two
arguments: ``context`` and ``request``.
For example:
.. code-block:: python
def some_other_framework(user_supplied_view):
config = Configurator(reg)
proxy_view = config.derive_view(user_supplied_view)
def my_wrapper(context, request):
do_something_that_mutates(request)
return proxy_view(context, request)
config.add_view(my_wrapper)
The ``view`` object provided should be one of the following:
- A function or another non-class callable object that accepts
a :term:`request` as a single positional argument and which
returns a :term:`response` object.
- A function or other non-class callable object that accepts
two positional arguments, ``context, request`` and which
returns a :term:`response` object.
- A class which accepts a single positional argument in its
constructor named ``request``, and which has a ``__call__``
method that accepts no arguments that returns a
:term:`response` object.
- A class which accepts two positional arguments named
``context, request``, and which has a ``__call__`` method
that accepts no arguments that returns a :term:`response`
object.
- A :term:`dotted Python name` which refers to any of the
kinds of objects above.
This API returns a callable which accepts the arguments
``context, request`` and which returns the result of calling
the provided ``view`` object.
The ``attr`` keyword argument is most useful when the view
object is a class. It names the method that should be used as
the callable. If ``attr`` is not provided, the attribute
effectively defaults to ``__call__``. See
:ref:`class_as_view` for more information.
The ``renderer`` keyword argument should be a renderer
name. If supplied, it will cause the returned callable to use
a :term:`renderer` to convert the user-supplied view result to
a :term:`response` object. If a ``renderer`` argument is not
supplied, the user-supplied view must itself return a
:term:`response` object. """
return self._derive_view(view, attr=attr, renderer=renderer)
# b/w compat
def _derive_view(self, view, permission=None, predicates=(),
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH, decorator=None,
mapper=None, http_cache=None):
view = self.maybe_dotted(view)
mapper = self.maybe_dotted(mapper)
if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
registry = self.registry)
if renderer is None:
# use default renderer if one exists
if self.registry.queryUtility(IRendererFactory) is not None:
renderer = renderers.RendererHelper(
name=None,
package=self.package,
registry=self.registry)
deriver = ViewDeriver(registry=self.registry,
permission=permission,
predicates=predicates,
attr=attr,
renderer=renderer,
wrapper_viewname=wrapper_viewname,
viewname=viewname,
accept=accept,
order=order,
phash=phash,
package=self.package,
mapper=mapper,
decorator=decorator,
http_cache=http_cache)
return deriver(view)
@action_method
def add_forbidden_view(
self,
view=None,
attr=None,
renderer=None,
wrapper=None,
route_name=None,
request_type=None,
request_method=None,
request_param=None,
containment=None,
xhr=None,
accept=None,
header=None,
path_info=None,
custom_predicates=(),
decorator=None,
mapper=None,
match_param=None,
**predicates
):
""" Add a forbidden view to the current configuration state. The
view will be called when Pyramid or application code raises a
:exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of
circumstances implied by the predicates provided are matched. The
simplest example is:
.. code-block:: python
def forbidden(request):
return Response('Forbidden', status='403 Forbidden')
config.add_forbidden_view(forbidden)
All arguments have the same meaning as
:meth:`pyramid.config.Configurator.add_view` and each predicate
argument restricts the set of circumstances under which this notfound
view will be invoked. Unlike
:meth:`pyramid.config.Configurator.add_view`, this method will raise
an exception if passed ``name``, ``permission``, ``context``,
``for_``, or ``http_cache`` keyword arguments. These argument values
make no sense in the context of a forbidden view.
.. note::
This method is new as of Pyramid 1.3.
"""
for arg in ('name', 'permission', 'context', 'for_', 'http_cache'):
if arg in predicates:
raise ConfigurationError(
'%s may not be used as an argument to add_forbidden_view'
% arg
)
settings = dict(
view=view,
context=HTTPForbidden,
wrapper=wrapper,
request_type=request_type,
request_method=request_method,
request_param=request_param,
containment=containment,
xhr=xhr,
accept=accept,
header=header,
path_info=path_info,
custom_predicates=custom_predicates,
decorator=decorator,
mapper=mapper,
match_param=match_param,
route_name=route_name,
permission=NO_PERMISSION_REQUIRED,
attr=attr,
renderer=renderer,
)
settings.update(predicates)
return self.add_view(**settings)
set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias
@action_method
def add_notfound_view(
self,
view=None,
attr=None,
renderer=None,
wrapper=None,
route_name=None,
request_type=None,
request_method=None,
request_param=None,
containment=None,
xhr=None,
accept=None,
header=None,
path_info=None,
custom_predicates=(),
decorator=None,
mapper=None,
match_param=None,
append_slash=False,
**predicates
):
""" Add a default notfound view to the current configuration state.
The view will be called when Pyramid or application code raises an
:exc:`pyramid.httpexceptions.HTTPForbidden` exception (e.g. when a
view cannot be found for the request). The simplest example is:
.. code-block:: python
def notfound(request):
return Response('Not Found', status='404 Not Found')
config.add_notfound_view(notfound)
All arguments except ``append_slash`` have the same meaning as
:meth:`pyramid.config.Configurator.add_view` and each predicate
argument restricts the set of circumstances under which this notfound
view will be invoked. Unlike
:meth:`pyramid.config.Configurator.add_view`, this method will raise
an exception if passed ``name``, ``permission``, ``context``,
``for_``, or ``http_cache`` keyword arguments. These argument values
make no sense in the context of a notfound view.
If ``append_slash`` is ``True``, when this notfound view is invoked,
and the current path info does not end in a slash, the notfound logic
will attempt to find a :term:`route` that matches the request's path
info suffixed with a slash. If such a route exists, Pyramid will
issue a redirect to the URL implied by the route; if it does not,
Pyramid will return the result of the view callable provided as
``view``, as normal.
.. note::
This method is new as of Pyramid 1.3.
"""
for arg in ('name', 'permission', 'context', 'for_', 'http_cache'):
if arg in predicates:
raise ConfigurationError(
'%s may not be used as an argument to add_notfound_view'
% arg
)
settings = dict(
view=view,
context=HTTPNotFound,
wrapper=wrapper,
request_type=request_type,
request_method=request_method,
request_param=request_param,
containment=containment,
xhr=xhr,
accept=accept,
header=header,
path_info=path_info,
custom_predicates=custom_predicates,
decorator=decorator,
mapper=mapper,
match_param=match_param,
route_name=route_name,
permission=NO_PERMISSION_REQUIRED,
)
settings.update(predicates)
if append_slash:
view = self._derive_view(view, attr=attr, renderer=renderer)
view = AppendSlashNotFoundViewFactory(view)
settings['view'] = view
else:
settings['attr'] = attr
settings['renderer'] = renderer
return self.add_view(**settings)
set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias
@action_method
def set_view_mapper(self, mapper):
"""
Setting a :term:`view mapper` makes it possible to make use of
:term:`view callable` objects which implement different call
signatures than the ones supported by :app:`Pyramid` as described in
its narrative documentation.
The ``mapper`` should argument be an object implementing
:class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted
Python name` to such an object. The provided ``mapper`` will become
the default view mapper to be used by all subsequent :term:`view
configuration` registrations.
See also :ref:`using_a_view_mapper`.
.. note::
Using the ``default_view_mapper`` argument to the
:class:`pyramid.config.Configurator` constructor
can be used to achieve the same purpose.
"""
mapper = self.maybe_dotted(mapper)
def register():
self.registry.registerUtility(mapper, IViewMapperFactory)
# IViewMapperFactory is looked up as the result of view config
# in phase 3
intr = self.introspectable('view mappers',
IViewMapperFactory,
self.object_description(mapper),
'default view mapper')
intr['mapper'] = mapper
self.action(IViewMapperFactory, register, order=PHASE1_CONFIG,
introspectables=(intr,))
@action_method
def add_static_view(self, name, path, **kw):
""" Add a view used to render static assets such as images
and CSS files.
The ``name`` argument is a string representing an
application-relative local URL prefix. It may alternately be a full
URL.
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:`asset specification`.
The ``cache_max_age`` keyword argument is input to set the
``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
:data:`pyramid.security.NO_PERMISSION_REQUIRED`, 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. If ``permission`` is specified, the security checking will
be performed against the default root factory ACL.
Any other keyword arguments sent to ``add_static_view`` are passed on
to :meth:`pyramid.config.Configurator.add_route` (e.g. ``factory``,
perhaps to define a custom factory with a custom ACL for this static
view).
*Usage*
The ``add_static_view`` function is typically used in conjunction
with the :meth:`pyramid.request.Request.static_url` method.
``add_static_view`` adds a view which renders a static asset when
some URL is visited; :meth:`pyramid.request.Request.static_url`
generates a URL to that asset.
The ``name`` argument to ``add_static_view`` is usually a simple URL
prefix (e.g. ``'images'``). When this is the case, the
:meth:`pyramid.request.Request.static_url` API will generate a URL
which points to a Pyramid view, which will serve up a set of assets
that live in the package itself. For example:
.. code-block:: python
add_static_view('images', 'mypackage:images/')
Code that registers such a view can generate URLs to the view via
:meth:`pyramid.request.Request.static_url`:
.. code-block:: python
request.static_url('mypackage:images/logo.png')
When ``add_static_view`` is called with a ``name`` argument that
represents a URL prefix, as it is above, subsequent calls to
:meth:`pyramid.request.Request.static_url` with paths that start with
the ``path`` argument passed to ``add_static_view`` will generate a
URL something like ``http:///images/logo.png``,
which will cause the ``logo.png`` file in the ``images`` subdirectory
of the ``mypackage`` package to be served.
``add_static_view`` can alternately be used with a ``name`` argument
which is a *URL*, causing static assets to be served from an external
webserver. This happens when the ``name`` argument is a fully
qualified URL (e.g. starts with ``http://`` or similar). In this
mode, the ``name`` is used as the prefix of the full URL when
generating a URL using :meth:`pyramid.request.Request.static_url`.
For example, if ``add_static_view`` is called like so:
.. code-block:: python
add_static_view('http://example.com/images', 'mypackage:images/')
Subsequently, the URLs generated by
:meth:`pyramid.request.Request.static_url` for that static view will
be prefixed with ``http://example.com/images``:
.. code-block:: python
static_url('mypackage:images/logo.png', request)
When ``add_static_view`` is called with a ``name`` argument that is
the URL ``http://example.com/images``, subsequent calls to
:meth:`pyramid.request.Request.static_url` with paths that start with
the ``path`` argument passed to ``add_static_view`` will generate a
URL something like ``http://example.com/logo.png``. The external
webserver listening on ``example.com`` must be itself configured to
respond properly to such a request.
See :ref:`static_assets_section` for more information.
"""
spec = self._make_spec(path)
info = self.registry.queryUtility(IStaticURLInfo)
if info is None:
info = StaticURLInfo()
self.registry.registerUtility(info, IStaticURLInfo)
info.add(self, name, spec, **kw)
def isexception(o):
if IInterface.providedBy(o):
if IException.isEqualOrExtendedBy(o):
return True
return (
isinstance(o, Exception) or
(inspect.isclass(o) and (issubclass(o, Exception)))
)
@implementer(IStaticURLInfo)
class StaticURLInfo(object):
def _get_registrations(self, registry):
try:
reg = registry._static_url_registrations
except AttributeError:
reg = registry._static_url_registrations = []
return reg
def generate(self, path, request, **kw):
try:
registry = request.registry
except AttributeError: # bw compat (for tests)
registry = get_current_registry()
for (url, spec, route_name) in self._get_registrations(registry):
if path.startswith(spec):
subpath = path[len(spec):]
if WIN: # pragma: no cover
subpath = subpath.replace('\\', '/') # windows
if url is None:
kw['subpath'] = subpath
return request.route_url(route_name, **kw)
else:
subpath = url_quote(subpath)
return urljoin(url, subpath)
raise ValueError('No static URL definition matching %s' % path)
def add(self, config, name, spec, **extra):
# This feature only allows for the serving of a directory and
# 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):]``).
if os.path.isabs(spec): # FBO windows
sep = os.sep
else:
sep = '/'
if not spec.endswith(sep):
spec = spec + sep
# we also make sure the name ends with a slash, purely as a
# convenience: a name that is a url is required to end in a
# slash, so that ``urljoin(name, subpath))`` will work above
# when the name is a URL, and it doesn't hurt things for it to
# have a name that ends in a slash if it's used as a route
# name instead of a URL.
if not name.endswith('/'):
# make sure it ends with a slash
name = name + '/'
if url_parse(name)[0]:
# it's a URL
# url, spec, route_name
url = name
route_name = None
else:
# it's a view name
url = None
cache_max_age = extra.pop('cache_max_age', None)
# create a view
view = static_view(spec, cache_max_age=cache_max_age,
use_subpath=True)
# Mutate extra to allow factory, etc to be passed through here.
# Treat permission specially because we'd like to default to
# permissiveness (see docs of config.add_static_view). We need
# to deal with both ``view_permission`` and ``permission``
# because ``permission`` is used in the docs for add_static_view,
# but ``add_route`` prefers ``view_permission``
permission = extra.pop('view_permission', None)
if permission is None:
permission = extra.pop('permission', None)
if permission is None:
permission = NO_PERMISSION_REQUIRED
context = extra.pop('view_context', None)
if context is None:
context = extra.pop('view_for', None)
if context is None:
context = extra.pop('for_', None)
renderer = extra.pop('view_renderer', None)
if renderer is None:
renderer = extra.pop('renderer', None)
attr = extra.pop('view_attr', None)
# register a route using the computed view, permission, and
# pattern, plus any extras passed to us via add_static_view
pattern = "%s*subpath" % name # name already ends with slash
if config.route_prefix:
route_name = '__%s/%s' % (config.route_prefix, name)
else:
route_name = '__%s' % name
config.add_route(route_name, pattern, **extra)
config.add_view(
route_name=route_name,
view=view,
permission=permission,
context=context,
renderer=renderer,
attr=attr
)
def register():
registrations = self._get_registrations(config.registry)
names = [ t[0] for t in registrations ]
if name in names:
idx = names.index(name)
registrations.pop(idx)
# url, spec, route_name
registrations.append((url, spec, route_name))
intr = config.introspectable('static views',
name,
'static view for %r' % name,
'static view')
intr['name'] = name
intr['spec'] = spec
config.action(None, callable=register, introspectables=(intr,))
pyramid-1.4.5/pyramid/config/assets.py 0000664 0001750 0001750 00000024420 12203712502 017244 0 ustar takaki takaki import pkg_resources
import sys
from zope.interface import implementer
from pyramid.interfaces import IPackageOverrides
from pyramid.exceptions import ConfigurationError
from pyramid.threadlocal import get_current_registry
from pyramid.util import action_method
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)
@implementer(IPackageOverrides)
class PackageOverrides(object):
# pkg_resources arg in kw args below for testing
def __init__(self, package, pkg_resources=pkg_resources):
loader = self._real_loader = getattr(package, '__loader__', None)
if isinstance(loader, self.__class__):
self._real_loader = None
# 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)
@property
def real_loader(self):
if self._real_loader is None:
raise NotImplementedError()
return self._real_loader
def get_data(self, path):
""" See IPEP302Loader.
"""
return self.real_loader.get_data(path)
def is_package(self, fullname):
""" See IPEP302Loader.
"""
return self.real_loader.is_package(fullname)
def get_code(self, fullname):
""" See IPEP302Loader.
"""
return self.real_loader.get_code(fullname)
def get_source(self, fullname):
""" See IPEP302Loader.
"""
return self.real_loader.get_source(fullname)
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
class AssetsConfiguratorMixin(object):
def _override(self, package, path, override_package, override_prefix,
PackageOverrides=PackageOverrides):
pkg_name = package.__name__
override_pkg_name = override_package.__name__
override = self.registry.queryUtility(IPackageOverrides, name=pkg_name)
if override is None:
override = PackageOverrides(package)
self.registry.registerUtility(override, IPackageOverrides,
name=pkg_name)
override.insert(path, override_pkg_name, override_prefix)
@action_method
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:`asset specification` to the
asset being overridden.
``override_with`` is a :term:`asset specification` to the
asset that is performing the override.
See :ref:`assets_chapter` for more
information about asset overrides."""
if to_override == override_with:
raise ConfigurationError('You cannot override an asset with itself')
package = to_override
path = ''
if ':' in to_override:
package, path = to_override.split(':', 1)
override_package = override_with
override_prefix = ''
if ':' in override_with:
override_package, override_prefix = override_with.split(':', 1)
# *_isdir = override is package or directory
overridden_isdir = path=='' or path.endswith('/')
override_isdir = override_prefix=='' or override_prefix.endswith('/')
if overridden_isdir and (not override_isdir):
raise ConfigurationError(
'A directory cannot be overridden with a file (put a '
'slash at the end of override_with if necessary)')
if (not overridden_isdir) and override_isdir:
raise ConfigurationError(
'A file cannot be overridden with a directory (put a '
'slash at the end of to_override if necessary)')
override = _override or self._override # test jig
def register():
__import__(package)
__import__(override_package)
from_package = sys.modules[package]
to_package = sys.modules[override_package]
override(from_package, path, to_package, override_prefix)
intr = self.introspectable(
'asset overrides',
(package, override_package, path, override_prefix),
'%s -> %s' % (to_override, override_with),
'asset override',
)
intr['to_override'] = to_override
intr['override_with'] = override_with
self.action(None, register, introspectables=(intr,))
override_resource = override_asset # bw compat
pyramid-1.4.5/pyramid/config/zca.py 0000664 0001750 0001750 00000001601 12203712502 016513 0 ustar takaki takaki from pyramid.threadlocal import get_current_registry
class ZCAConfiguratorMixin(object):
def hook_zca(self):
""" Call :func:`zope.component.getSiteManager.sethook` with the
argument :data:`pyramid.threadlocal.get_current_registry`, causing
the :term:`Zope Component Architecture` 'global' APIs such as
:func:`zope.component.getSiteManager`,
:func:`zope.component.getAdapter` and others to use the
:app:`Pyramid` :term:`application registry` rather than the Zope
'global' registry."""
from zope.component import getSiteManager
getSiteManager.sethook(get_current_registry)
def unhook_zca(self):
""" Call :func:`zope.component.getSiteManager.reset` to undo the
action of :meth:`pyramid.config.Configurator.hook_zca`."""
from zope.component import getSiteManager
getSiteManager.reset()
pyramid-1.4.5/pyramid/config/security.py 0000664 0001750 0001750 00000015247 12203712502 017620 0 ustar takaki takaki from pyramid.interfaces import (
IAuthorizationPolicy,
IAuthenticationPolicy,
IDefaultPermission,
PHASE1_CONFIG,
PHASE2_CONFIG,
)
from pyramid.exceptions import ConfigurationError
from pyramid.util import action_method
class SecurityConfiguratorMixin(object):
@action_method
def set_authentication_policy(self, policy):
""" Override the :app:`Pyramid` :term:`authentication policy` in the
current configuration. The ``policy`` argument must be an instance
of an authentication policy or a :term:`dotted Python name`
that points at an instance of an authentication policy.
.. note::
Using the ``authentication_policy`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
def register():
self._set_authentication_policy(policy)
if self.registry.queryUtility(IAuthorizationPolicy) is None:
raise ConfigurationError(
'Cannot configure an authentication policy without '
'also configuring an authorization policy '
'(use the set_authorization_policy method)')
intr = self.introspectable('authentication policy', None,
self.object_description(policy),
'authentication policy')
intr['policy'] = policy
# authentication policy used by view config (phase 3)
self.action(IAuthenticationPolicy, register, order=PHASE2_CONFIG,
introspectables=(intr,))
def _set_authentication_policy(self, policy):
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
@action_method
def set_authorization_policy(self, policy):
""" Override the :app:`Pyramid` :term:`authorization policy` in the
current configuration. The ``policy`` argument must be an instance
of an authorization policy or a :term:`dotted Python name` that points
at an instance of an authorization policy.
.. note::
Using the ``authorization_policy`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
def register():
self._set_authorization_policy(policy)
def ensure():
if self.autocommit:
return
if self.registry.queryUtility(IAuthenticationPolicy) is None:
raise ConfigurationError(
'Cannot configure an authorization policy without '
'also configuring an authentication policy '
'(use the set_authorization_policy method)')
intr = self.introspectable('authorization policy', None,
self.object_description(policy),
'authorization policy')
intr['policy'] = policy
# authorization policy used by view config (phase 3) and
# authentication policy (phase 2)
self.action(IAuthorizationPolicy, register, order=PHASE1_CONFIG,
introspectables=(intr,))
self.action(None, ensure)
def _set_authorization_policy(self, policy):
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthorizationPolicy)
@action_method
def set_default_permission(self, permission):
"""
Set the default permission to be used by all subsequent
:term:`view configuration` registrations. ``permission``
should be a :term:`permission` string to be used as the
default permission. An example of a permission
string:``'view'``. Adding a default permission makes it
unnecessary to protect each view configuration with an
explicit permission, unless your application policy requires
some exception for a particular view.
If a default permission is *not* set, views represented by
view configuration registrations which do not explicitly
declare a permission will be executable by entirely anonymous
users (any authorization policy is ignored).
Later calls to this method override will conflict with earlier calls;
there can be only one default permission active at a time within an
application.
.. warning::
If a default permission is in effect, view configurations meant to
create a truly anonymously accessible view (even :term:`exception
view` views) *must* use the value of the permission importable as
:data:`pyramid.security.NO_PERMISSION_REQUIRED`. When this string
is used as the ``permission`` for a view configuration, the default
permission is ignored, and the view is registered, making it
available to all callers regardless of their credentials.
See also :ref:`setting_a_default_permission`.
.. note::
Using the ``default_permission`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.
"""
def register():
self.registry.registerUtility(permission, IDefaultPermission)
intr = self.introspectable('default permission',
None,
permission,
'default permission')
intr['value'] = permission
perm_intr = self.introspectable('permissions',
permission,
permission,
'permission')
perm_intr['value'] = permission
# default permission used during view registration (phase 3)
self.action(IDefaultPermission, register, order=PHASE1_CONFIG,
introspectables=(intr, perm_intr,))
def add_permission(self, permission_name):
"""
A configurator directive which registers a free-standing
permission without associating it with a view callable. This can be
used so that the permission shows up in the introspectable data under
the ``permissions`` category (permissions mentioned via ``add_view``
already end up in there). For example::
config = Configurator()
config.add_permission('view')
"""
intr = self.introspectable(
'permissions',
permission_name,
permission_name,
'permission'
)
intr['value'] = permission_name
self.action(None, introspectables=(intr,))
pyramid-1.4.5/pyramid/config/settings.py 0000664 0001750 0001750 00000016565 12203712502 017615 0 ustar takaki takaki import os
import warnings
from zope.interface import implementer
from pyramid.interfaces import ISettings
from pyramid.settings import asbool
class SettingsConfiguratorMixin(object):
def _set_settings(self, mapping):
if not mapping:
mapping = {}
settings = Settings(mapping)
self.registry.settings = settings
return settings
def add_settings(self, settings=None, **kw):
"""Augment the :term:`deployment settings` with one or more
key/value pairs.
You may pass a dictionary::
config.add_settings({'external_uri':'http://example.com'})
Or a set of key/value pairs::
config.add_settings(external_uri='http://example.com')
This function is useful when you need to test code that accesses the
:attr:`pyramid.registry.Registry.settings` API (or the
:meth:`pyramid.config.Configurator.get_settings` API) and
which uses values from that API.
"""
if settings is None:
settings = {}
utility = self.registry.settings
if utility is None:
utility = self._set_settings(settings)
utility.update(settings)
utility.update(kw)
def get_settings(self):
"""
Return a :term:`deployment settings` object for the current
application. A deployment settings object is a dictionary-like
object that contains key/value pairs based on the dictionary passed
as the ``settings`` argument to the
:class:`pyramid.config.Configurator` constructor.
.. note:: the :attr:`pyramid.registry.Registry.settings` API
performs the same duty.
"""
return self.registry.settings
@implementer(ISettings)
class Settings(dict):
""" Deployment settings. Update application settings (usually
from PasteDeploy keywords) with framework-specific key/value pairs
(e.g. find ``PYRAMID_DEBUG_AUTHORIZATION`` in os.environ and jam into
keyword args)."""
# _environ_ is dep inj for testing
def __init__(self, d=None, _environ_=os.environ, **kw):
if d is None:
d = {}
dict.__init__(self, d, **kw)
eget = _environ_.get
config_debug_all = self.get('debug_all', '')
config_debug_all = self.get('pyramid.debug_all', config_debug_all)
eff_debug_all = asbool(eget('PYRAMID_DEBUG_ALL', config_debug_all))
config_reload_all = self.get('reload_all', '')
config_reload_all = self.get('pyramid.reload_all', config_reload_all)
eff_reload_all = asbool(eget('PYRAMID_RELOAD_ALL', config_reload_all))
config_debug_auth = self.get('debug_authorization', '')
config_debug_auth = self.get('pyramid.debug_authorization',
config_debug_auth)
eff_debug_auth = asbool(eget('PYRAMID_DEBUG_AUTHORIZATION',
config_debug_auth))
config_debug_notfound = self.get('debug_notfound', '')
config_debug_notfound = self.get('pyramid.debug_notfound',
config_debug_notfound)
eff_debug_notfound = asbool(eget('PYRAMID_DEBUG_NOTFOUND',
config_debug_notfound))
config_debug_routematch = self.get('debug_routematch', '')
config_debug_routematch = self.get('pyramid.debug_routematch',
config_debug_routematch)
eff_debug_routematch = asbool(eget('PYRAMID_DEBUG_ROUTEMATCH',
config_debug_routematch))
config_debug_templates = self.get('debug_templates', '')
config_debug_templates = self.get('pyramid.debug_templates',
config_debug_templates)
eff_debug_templates = asbool(eget('PYRAMID_DEBUG_TEMPLATES',
config_debug_templates))
config_reload_templates = self.get('reload_templates', '')
config_reload_templates = self.get('pyramid.reload_templates',
config_reload_templates)
eff_reload_templates = asbool(eget('PYRAMID_RELOAD_TEMPLATES',
config_reload_templates))
config_reload_assets = self.get('reload_assets', '')
config_reload_assets = self.get('pyramid.reload_assets',
config_reload_assets)
reload_assets = asbool(eget('PYRAMID_RELOAD_ASSETS',
config_reload_assets))
config_reload_resources = self.get('reload_resources', '')
config_reload_resources = self.get('pyramid.reload_resources',
config_reload_resources)
reload_resources = asbool(eget('PYRAMID_RELOAD_RESOURCES',
config_reload_resources))
# reload_resources is an older alias for reload_assets
eff_reload_assets = reload_assets or reload_resources
locale_name = self.get('default_locale_name', 'en')
locale_name = self.get('pyramid.default_locale_name', locale_name)
eff_locale_name = eget('PYRAMID_DEFAULT_LOCALE_NAME', locale_name)
config_prevent_http_cache = self.get('prevent_http_cache', '')
config_prevent_http_cache = self.get('pyramid.prevent_http_cache',
config_prevent_http_cache)
eff_prevent_http_cache = asbool(eget('PYRAMID_PREVENT_HTTP_CACHE',
config_prevent_http_cache))
update = {
'debug_authorization': eff_debug_all or eff_debug_auth,
'debug_notfound': eff_debug_all or eff_debug_notfound,
'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_assets,
'reload_assets':eff_reload_all or eff_reload_assets,
'default_locale_name':eff_locale_name,
'prevent_http_cache':eff_prevent_http_cache,
'pyramid.debug_authorization': eff_debug_all or eff_debug_auth,
'pyramid.debug_notfound': eff_debug_all or eff_debug_notfound,
'pyramid.debug_routematch': eff_debug_all or eff_debug_routematch,
'pyramid.debug_templates': eff_debug_all or eff_debug_templates,
'pyramid.reload_templates': eff_reload_all or eff_reload_templates,
'pyramid.reload_resources':eff_reload_all or eff_reload_assets,
'pyramid.reload_assets':eff_reload_all or eff_reload_assets,
'pyramid.default_locale_name':eff_locale_name,
'pyramid.prevent_http_cache':eff_prevent_http_cache,
}
self.update(update)
def __getattr__(self, name):
try:
val = self[name]
# only deprecate on success; a probing getattr/hasattr should not
# print this warning
warnings.warn(
'Obtaining settings via attributes of the settings dictionary '
'is deprecated as of Pyramid 1.2; use settings["foo"] instead '
'of settings.foo',
DeprecationWarning,
2
)
return val
except KeyError:
raise AttributeError(name)
pyramid-1.4.5/pyramid/paster.py 0000664 0001750 0001750 00000011674 12210154276 016010 0 ustar takaki takaki import os
from paste.deploy import (
loadapp,
appconfig,
)
from pyramid.compat import configparser
from logging.config import fileConfig
from pyramid.scripting import prepare
def get_app(config_uri, name=None, options=None, loadapp=loadapp):
""" Return the WSGI application named ``name`` in the PasteDeploy
config file specified by ``config_uri``.
``options``, if passed, should be a dictionary used as variable assignments
like ``{'http_port': 8080}``. This is useful if e.g. ``%(http_port)s`` is
used in the config file.
If the ``name`` is None, this will attempt to parse the name from
the ``config_uri`` string expecting the format ``inifile#name``.
If no name is found, the name will default to "main"."""
path, section = _getpathsec(config_uri, name)
config_name = 'config:%s' % path
here_dir = os.getcwd()
if options:
kw = {'global_conf': options}
else:
kw = {}
app = loadapp(config_name, name=section, relative_to=here_dir, **kw)
return app
def get_appsettings(config_uri, name=None, appconfig=appconfig):
""" Return a dictionary representing the key/value pairs in an ``app``
section within the file represented by ``config_uri``.
If the ``name`` is None, this will attempt to parse the name from
the ``config_uri`` string expecting the format ``inifile#name``.
If no name is found, the name will default to "main"."""
path, section = _getpathsec(config_uri, name)
config_name = 'config:%s' % path
here_dir = os.getcwd()
return appconfig(config_name, name=section, relative_to=here_dir)
def setup_logging(config_uri, fileConfig=fileConfig,
configparser=configparser):
"""
Set up logging via the logging module's fileConfig function with the
filename specified via ``config_uri`` (a string in the form
``filename#sectionname``).
ConfigParser defaults are specified for the special ``__file__``
and ``here`` variables, similar to PasteDeploy config loading.
"""
path, _ = _getpathsec(config_uri, None)
parser = configparser.ConfigParser()
parser.read([path])
if parser.has_section('loggers'):
config_file = os.path.abspath(path)
return fileConfig(
config_file,
dict(__file__=config_file, here=os.path.dirname(config_file))
)
def _getpathsec(config_uri, name):
if '#' in config_uri:
path, section = config_uri.split('#', 1)
else:
path, section = config_uri, 'main'
if name:
section = name
return path, section
def bootstrap(config_uri, request=None, options=None):
""" Load a WSGI application from the PasteDeploy config file specified
by ``config_uri``. The environment will be configured as if it is
currently serving ``request``, leaving a natural environment in place
to write scripts that can generate URLs and utilize renderers.
This function returns a dictionary with ``app``, ``root``, ``closer``,
``request``, and ``registry`` keys. ``app`` is the WSGI app loaded
(based on the ``config_uri``), ``root`` is the traversal root resource
of the Pyramid application, and ``closer`` is a parameterless callback
that may be called when your script is complete (it pops a threadlocal
stack).
.. note::
Most operations within :app:`Pyramid` expect to be invoked within the
context of a WSGI request, thus it's important when loading your
application to anchor it when executing scripts and other code that is
not normally invoked during active WSGI requests.
.. note::
For a complex config file containing multiple :app:`Pyramid`
applications, this function will setup the environment under the context
of the last-loaded :app:`Pyramid` application. You may load a specific
application yourself by using the lower-level functions
:meth:`pyramid.paster.get_app` and :meth:`pyramid.scripting.prepare` in
conjunction with :attr:`pyramid.config.global_registries`.
``config_uri`` -- specifies the PasteDeploy config file to use for the
interactive shell. The format is ``inifile#name``. If the name is left
off, ``main`` will be assumed.
``request`` -- specified to anchor the script to a given set of WSGI
parameters. For example, most people would want to specify the host,
scheme and port such that their script will generate URLs in relation
to those parameters. A request with default parameters is constructed
for you if none is provided. You can mutate the request's ``environ``
later to setup a specific host/port/scheme/etc.
``options`` Is passed to get_app for use as variable assignments like
{'http_port': 8080} and then use %(http_port)s in the
config file.
See :ref:`writing_a_script` for more information about how to use this
function.
"""
app = get_app(config_uri, options=options)
env = prepare(request)
env['app'] = app
return env
pyramid-1.4.5/pyramid/static.py 0000664 0001750 0001750 00000013122 12203712502 015761 0 ustar takaki takaki # -*- coding: utf-8 -*-
import os
from os.path import (
normcase,
normpath,
join,
isdir,
exists,
)
from pkg_resources import (
resource_exists,
resource_filename,
resource_isdir,
)
from repoze.lru import lru_cache
from pyramid.asset import resolve_asset_spec
from pyramid.compat import text_
from pyramid.httpexceptions import (
HTTPNotFound,
HTTPMovedPermanently,
)
from pyramid.path import caller_package
from pyramid.response import FileResponse
from pyramid.traversal import traversal_path_info
slash = text_('/')
class static_view(object):
""" An instance of this class is a callable which can act as a
:app:`Pyramid` :term:`view callable`; this view will serve
static files from a directory on disk based on the ``root_dir``
you provide to its constructor.
The directory may contain subdirectories (recursively); the static
view implementation will descend into these directories as
necessary based on the components of the URL in order to resolve a
path into a response.
You may pass an absolute or relative filesystem path or a
:term:`asset specification` representing the directory
containing static files as the ``root_dir`` argument to this
class' constructor.
If the ``root_dir`` path is relative, and the ``package_name``
argument is ``None``, ``root_dir`` will be considered relative to
the directory in which the Python file which *calls* ``static``
resides. If the ``package_name`` name argument is provided, and a
relative ``root_dir`` is provided, the ``root_dir`` will be
considered relative to the Python :term:`package` specified by
``package_name`` (a dotted path to a Python package).
``cache_max_age`` influences the ``Expires`` and ``Max-Age``
response headers returned by the view (default is 3600 seconds or
five minutes).
``use_subpath`` influences whether ``request.subpath`` will be used as
``PATH_INFO`` when calling the underlying WSGI application which actually
serves the static files. If it is ``True``, the static application will
consider ``request.subpath`` as ``PATH_INFO`` input. If it is ``False``,
the static application will consider request.environ[``PATH_INFO``] as
``PATH_INFO`` input. By default, this is ``False``.
.. note::
If the ``root_dir`` is relative to a :term:`package`, or is a
:term:`asset specification` the :app:`Pyramid`
:class:`pyramid.config.Configurator` method can be used to override
assets within the named ``root_dir`` package-relative directory.
However, if the ``root_dir`` is absolute, configuration will not be able
to override the assets it contains.
"""
def __init__(self, root_dir, cache_max_age=3600, package_name=None,
use_subpath=False, index='index.html'):
# package_name is for bw compat; it is preferred to pass in a
# package-relative path as root_dir
# (e.g. ``anotherpackage:foo/static``).
self.cache_max_age = cache_max_age
if package_name is None:
package_name = caller_package().__name__
package_name, docroot = resolve_asset_spec(root_dir, package_name)
self.use_subpath = use_subpath
self.package_name = package_name
self.docroot = docroot
self.norm_docroot = normcase(normpath(docroot))
self.index = index
def __call__(self, context, request):
if self.use_subpath:
path_tuple = request.subpath
else:
path_tuple = traversal_path_info(request.environ['PATH_INFO'])
path = _secure_path(path_tuple)
if path is None:
raise HTTPNotFound('Out of bounds: %s' % request.url)
if self.package_name: # package resource
resource_path ='%s/%s' % (self.docroot.rstrip('/'), path)
if resource_isdir(self.package_name, resource_path):
if not request.path_url.endswith('/'):
self.add_slash_redirect(request)
resource_path = '%s/%s' % (resource_path.rstrip('/'),self.index)
if not resource_exists(self.package_name, resource_path):
raise HTTPNotFound(request.url)
filepath = resource_filename(self.package_name, resource_path)
else: # filesystem file
# os.path.normpath converts / to \ on windows
filepath = normcase(normpath(join(self.norm_docroot, path)))
if isdir(filepath):
if not request.path_url.endswith('/'):
self.add_slash_redirect(request)
filepath = join(filepath, self.index)
if not exists(filepath):
raise HTTPNotFound(request.url)
return FileResponse(filepath, request, self.cache_max_age)
def add_slash_redirect(self, request):
url = request.path_url + '/'
qs = request.query_string
if qs:
url = url + '?' + qs
raise HTTPMovedPermanently(url)
_seps = set(['/', os.sep])
def _contains_slash(item):
for sep in _seps:
if sep in item:
return True
_has_insecure_pathelement = set(['..', '.', '']).intersection
@lru_cache(1000)
def _secure_path(path_tuple):
if _has_insecure_pathelement(path_tuple):
# belt-and-suspenders security; this should never be true
# unless someone screws up the traversal_path code
# (request.subpath is computed via traversal_path too)
return None
if any([_contains_slash(item) for item in path_tuple]):
return None
encoded = slash.join(path_tuple) # will be unicode
return encoded
pyramid-1.4.5/pyramid/authorization.py 0000664 0001750 0001750 00000013767 12210154276 017417 0 ustar takaki takaki from zope.interface import implementer
from pyramid.interfaces import IAuthorizationPolicy
from pyramid.location import lineage
from pyramid.compat import is_nonstr_iter
from pyramid.security import (
ACLAllowed,
ACLDenied,
Allow,
Deny,
Everyone,
)
@implementer(IAuthorizationPolicy)
class ACLAuthorizationPolicy(object):
""" An :term:`authorization policy` which consults an :term:`ACL`
object attached to a :term:`context` to determine authorization
information about a :term:`principal` or multiple principals.
If the context is part of a :term:`lineage`, the context's parents
are consulted for ACL information too. The following is true
about this security policy.
- When checking whether the 'current' user is permitted (via the
``permits`` method), the security policy consults the
``context`` for an ACL first. If no ACL exists on the context,
or one does exist but the ACL does not explicitly allow or deny
access for any of the effective principals, consult the
context's parent ACL, and so on, until the lineage is exhausted
or we determine that the policy permits or denies.
During this processing, if any :data:`pyramid.security.Deny`
ACE is found matching any principal in ``principals``, stop
processing by returning an
:class:`pyramid.security.ACLDenied` instance (equals
``False``) immediately. If any
:data:`pyramid.security.Allow` ACE is found matching any
principal, stop processing by returning an
:class:`pyramid.security.ACLAllowed` instance (equals
``True``) immediately. If we exhaust the context's
:term:`lineage`, and no ACE has explicitly permitted or denied
access, return an instance of
:class:`pyramid.security.ACLDenied` (equals ``False``).
- When computing principals allowed by a permission via the
:func:`pyramid.security.principals_allowed_by_permission`
method, we compute the set of principals that are explicitly
granted the ``permission`` in the provided ``context``. We do
this by walking 'up' the object graph *from the root* to the
context. During this walking process, if we find an explicit
:data:`pyramid.security.Allow` ACE for a principal that
matches the ``permission``, the principal is included in the
allow list. However, if later in the walking process that
principal is mentioned in any :data:`pyramid.security.Deny`
ACE for the permission, the principal is removed from the allow
list. If a :data:`pyramid.security.Deny` to the principal
:data:`pyramid.security.Everyone` is encountered during the
walking process that matches the ``permission``, the allow list
is cleared for all principals encountered in previous ACLs. The
walking process ends after we've processed the any ACL directly
attached to ``context``; a set of principals is returned.
Objects of this class implement the
:class:`pyramid.interfaces.IAuthorizationPolicy` interface.
"""
def permits(self, context, principals, permission):
""" Return an instance of
:class:`pyramid.security.ACLAllowed` instance if the policy
permits access, return an instance of
:class:`pyramid.security.ACLDenied` if not."""
acl = ''
for location in lineage(context):
try:
acl = location.__acl__
except AttributeError:
continue
for ace in acl:
ace_action, ace_principal, ace_permissions = ace
if ace_principal in principals:
if not is_nonstr_iter(ace_permissions):
ace_permissions = [ace_permissions]
if permission in ace_permissions:
if ace_action == Allow:
return ACLAllowed(ace, acl, permission,
principals, location)
else:
return ACLDenied(ace, acl, permission,
principals, location)
# default deny (if no ACL in lineage at all, or if none of the
# principals were mentioned in any ACE we found)
return ACLDenied(
'',
acl,
permission,
principals,
context)
def principals_allowed_by_permission(self, context, permission):
""" Return the set of principals explicitly granted the
permission named ``permission`` according to the ACL directly
attached to the ``context`` as well as inherited ACLs based on
the :term:`lineage`."""
allowed = set()
for location in reversed(list(lineage(context))):
# NB: we're walking *up* the object graph from the root
try:
acl = location.__acl__
except AttributeError:
continue
allowed_here = set()
denied_here = set()
for ace_action, ace_principal, ace_permissions in acl:
if not is_nonstr_iter(ace_permissions):
ace_permissions = [ace_permissions]
if (ace_action == Allow) and (permission in ace_permissions):
if not ace_principal in denied_here:
allowed_here.add(ace_principal)
if (ace_action == Deny) and (permission in ace_permissions):
denied_here.add(ace_principal)
if ace_principal == Everyone:
# clear the entire allowed set, as we've hit a
# deny of Everyone ala (Deny, Everyone, ALL)
allowed = set()
break
elif ace_principal in allowed:
allowed.remove(ace_principal)
allowed.update(allowed_here)
return allowed
pyramid-1.4.5/pyramid/tests/ 0000775 0001750 0001750 00000000000 12210157153 015266 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/test_util.py 0000664 0001750 0001750 00000051742 12203712502 017662 0 ustar takaki takaki import unittest
from pyramid.compat import PY3
class Test_InstancePropertyMixin(unittest.TestCase):
def _makeOne(self):
cls = self._getTargetClass()
class Foo(cls):
pass
return Foo()
def _getTargetClass(self):
from pyramid.util import InstancePropertyMixin
return InstancePropertyMixin
def test_callable(self):
def worker(obj):
return obj.bar
foo = self._makeOne()
foo.set_property(worker)
foo.bar = 1
self.assertEqual(1, foo.worker)
foo.bar = 2
self.assertEqual(2, foo.worker)
def test_callable_with_name(self):
def worker(obj):
return obj.bar
foo = self._makeOne()
foo.set_property(worker, name='x')
foo.bar = 1
self.assertEqual(1, foo.x)
foo.bar = 2
self.assertEqual(2, foo.x)
def test_callable_with_reify(self):
def worker(obj):
return obj.bar
foo = self._makeOne()
foo.set_property(worker, reify=True)
foo.bar = 1
self.assertEqual(1, foo.worker)
foo.bar = 2
self.assertEqual(1, foo.worker)
def test_callable_with_name_reify(self):
def worker(obj):
return obj.bar
foo = self._makeOne()
foo.set_property(worker, name='x')
foo.set_property(worker, name='y', reify=True)
foo.bar = 1
self.assertEqual(1, foo.y)
self.assertEqual(1, foo.x)
foo.bar = 2
self.assertEqual(2, foo.x)
self.assertEqual(1, foo.y)
def test_property_without_name(self):
def worker(obj): pass
foo = self._makeOne()
self.assertRaises(ValueError, foo.set_property, property(worker))
def test_property_with_name(self):
def worker(obj):
return obj.bar
foo = self._makeOne()
foo.set_property(property(worker), name='x')
foo.bar = 1
self.assertEqual(1, foo.x)
foo.bar = 2
self.assertEqual(2, foo.x)
def test_property_with_reify(self):
def worker(obj): pass
foo = self._makeOne()
self.assertRaises(ValueError, foo.set_property,
property(worker), name='x', reify=True)
def test_override_property(self):
def worker(obj): pass
foo = self._makeOne()
foo.set_property(worker, name='x')
def doit():
foo.x = 1
self.assertRaises(AttributeError, doit)
def test_override_reify(self):
def worker(obj): pass
foo = self._makeOne()
foo.set_property(worker, name='x', reify=True)
foo.x = 1
self.assertEqual(1, foo.x)
foo.x = 2
self.assertEqual(2, foo.x)
def test_reset_property(self):
foo = self._makeOne()
foo.set_property(lambda _: 1, name='x')
self.assertEqual(1, foo.x)
foo.set_property(lambda _: 2, name='x')
self.assertEqual(2, foo.x)
def test_reset_reify(self):
""" This is questionable behavior, but may as well get notified
if it changes."""
foo = self._makeOne()
foo.set_property(lambda _: 1, name='x', reify=True)
self.assertEqual(1, foo.x)
foo.set_property(lambda _: 2, name='x', reify=True)
self.assertEqual(1, foo.x)
def test__make_property(self):
from pyramid.decorator import reify
cls = self._getTargetClass()
name, fn = cls._make_property(lambda x: 1, name='x', reify=True)
self.assertEqual(name, 'x')
self.assertTrue(isinstance(fn, reify))
def test__set_properties_with_iterable(self):
foo = self._makeOne()
x = foo._make_property(lambda _: 1, name='x', reify=True)
y = foo._make_property(lambda _: 2, name='y')
foo._set_properties([x, y])
self.assertEqual(1, foo.x)
self.assertEqual(2, foo.y)
def test__set_properties_with_dict(self):
foo = self._makeOne()
x_name, x_fn = foo._make_property(lambda _: 1, name='x', reify=True)
y_name, y_fn = foo._make_property(lambda _: 2, name='y')
foo._set_properties({x_name: x_fn, y_name: y_fn})
self.assertEqual(1, foo.x)
self.assertEqual(2, foo.y)
def test__set_extensions(self):
inst = self._makeOne()
def foo(self, result):
return result
n, bar = inst._make_property(lambda _: 'bar', name='bar')
class Extensions(object):
def __init__(self):
self.methods = {'foo':foo}
self.descriptors = {'bar':bar}
extensions = Extensions()
inst._set_extensions(extensions)
self.assertEqual(inst.bar, 'bar')
self.assertEqual(inst.foo('abc'), 'abc')
class Test_WeakOrderedSet(unittest.TestCase):
def _makeOne(self):
from pyramid.config import WeakOrderedSet
return WeakOrderedSet()
def test_ctor(self):
wos = self._makeOne()
self.assertEqual(len(wos), 0)
self.assertEqual(wos.last, None)
def test_add_item(self):
wos = self._makeOne()
reg = Dummy()
wos.add(reg)
self.assertEqual(list(wos), [reg])
self.assertTrue(reg in wos)
self.assertEqual(wos.last, reg)
def test_add_multiple_items(self):
wos = self._makeOne()
reg1 = Dummy()
reg2 = Dummy()
wos.add(reg1)
wos.add(reg2)
self.assertEqual(len(wos), 2)
self.assertEqual(list(wos), [reg1, reg2])
self.assertTrue(reg1 in wos)
self.assertTrue(reg2 in wos)
self.assertEqual(wos.last, reg2)
def test_add_duplicate_items(self):
wos = self._makeOne()
reg = Dummy()
wos.add(reg)
wos.add(reg)
self.assertEqual(len(wos), 1)
self.assertEqual(list(wos), [reg])
self.assertTrue(reg in wos)
self.assertEqual(wos.last, reg)
def test_weakref_removal(self):
wos = self._makeOne()
reg = Dummy()
wos.add(reg)
wos.remove(reg)
self.assertEqual(len(wos), 0)
self.assertEqual(list(wos), [])
self.assertEqual(wos.last, None)
def test_last_updated(self):
wos = self._makeOne()
reg = Dummy()
reg2 = Dummy()
wos.add(reg)
wos.add(reg2)
wos.remove(reg2)
self.assertEqual(len(wos), 1)
self.assertEqual(list(wos), [reg])
self.assertEqual(wos.last, reg)
def test_empty(self):
wos = self._makeOne()
reg = Dummy()
reg2 = Dummy()
wos.add(reg)
wos.add(reg2)
wos.empty()
self.assertEqual(len(wos), 0)
self.assertEqual(list(wos), [])
self.assertEqual(wos.last, None)
class Test_object_description(unittest.TestCase):
def _callFUT(self, object):
from pyramid.util import object_description
return object_description(object)
def test_string(self):
self.assertEqual(self._callFUT('abc'), 'abc')
def test_int(self):
self.assertEqual(self._callFUT(1), '1')
def test_bool(self):
self.assertEqual(self._callFUT(True), 'True')
def test_None(self):
self.assertEqual(self._callFUT(None), 'None')
def test_float(self):
self.assertEqual(self._callFUT(1.2), '1.2')
def test_tuple(self):
self.assertEqual(self._callFUT(('a', 'b')), "('a', 'b')")
def test_set(self):
if PY3: # pragma: no cover
self.assertEqual(self._callFUT(set(['a'])), "{'a'}")
else: # pragma: no cover
self.assertEqual(self._callFUT(set(['a'])), "set(['a'])")
def test_list(self):
self.assertEqual(self._callFUT(['a']), "['a']")
def test_dict(self):
self.assertEqual(self._callFUT({'a':1}), "{'a': 1}")
def test_nomodule(self):
o = object()
self.assertEqual(self._callFUT(o), 'object %s' % str(o))
def test_module(self):
import pyramid
self.assertEqual(self._callFUT(pyramid), 'module pyramid')
def test_method(self):
self.assertEqual(
self._callFUT(self.test_method),
'method test_method of class pyramid.tests.test_util.'
'Test_object_description')
def test_class(self):
self.assertEqual(
self._callFUT(self.__class__),
'class pyramid.tests.test_util.Test_object_description')
def test_function(self):
self.assertEqual(
self._callFUT(dummyfunc),
'function pyramid.tests.test_util.dummyfunc')
def test_instance(self):
inst = Dummy()
self.assertEqual(
self._callFUT(inst),
"object %s" % str(inst))
def test_shortened_repr(self):
inst = ['1'] * 1000
self.assertEqual(
self._callFUT(inst),
str(inst)[:100] + ' ... ]')
class TestTopologicalSorter(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from pyramid.util import TopologicalSorter
return TopologicalSorter(*arg, **kw)
def test_remove(self):
inst = self._makeOne()
inst.names.append('name')
inst.name2val['name'] = 1
inst.req_after.add('name')
inst.req_before.add('name')
inst.name2after['name'] = ('bob',)
inst.name2before['name'] = ('fred',)
inst.order.append(('bob', 'name'))
inst.order.append(('name', 'fred'))
inst.remove('name')
self.assertFalse(inst.names)
self.assertFalse(inst.req_before)
self.assertFalse(inst.req_after)
self.assertFalse(inst.name2before)
self.assertFalse(inst.name2after)
self.assertFalse(inst.name2val)
self.assertFalse(inst.order)
def test_add(self):
from pyramid.util import LAST
sorter = self._makeOne()
sorter.add('name', 'factory')
self.assertEqual(sorter.names, ['name'])
self.assertEqual(sorter.name2val,
{'name':'factory'})
self.assertEqual(sorter.order, [('name', LAST)])
sorter.add('name2', 'factory2')
self.assertEqual(sorter.names, ['name', 'name2'])
self.assertEqual(sorter.name2val,
{'name':'factory', 'name2':'factory2'})
self.assertEqual(sorter.order,
[('name', LAST), ('name2', LAST)])
sorter.add('name3', 'factory3', before='name2')
self.assertEqual(sorter.names,
['name', 'name2', 'name3'])
self.assertEqual(sorter.name2val,
{'name':'factory', 'name2':'factory2',
'name3':'factory3'})
self.assertEqual(sorter.order,
[('name', LAST), ('name2', LAST),
('name3', 'name2')])
def test_sorted_ordering_1(self):
sorter = self._makeOne()
sorter.add('name1', 'factory1')
sorter.add('name2', 'factory2')
self.assertEqual(sorter.sorted(),
[
('name1', 'factory1'),
('name2', 'factory2'),
])
def test_sorted_ordering_2(self):
from pyramid.util import FIRST
sorter = self._makeOne()
sorter.add('name1', 'factory1')
sorter.add('name2', 'factory2', after=FIRST)
self.assertEqual(sorter.sorted(),
[
('name2', 'factory2'),
('name1', 'factory1'),
])
def test_sorted_ordering_3(self):
from pyramid.util import FIRST
sorter = self._makeOne()
add = sorter.add
add('auth', 'auth_factory', after='browserid')
add('dbt', 'dbt_factory')
add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
add('browserid', 'browserid_factory')
add('txnmgr', 'txnmgr_factory', after='exceptionview')
add('exceptionview', 'excview_factory', after=FIRST)
self.assertEqual(sorter.sorted(),
[
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
('txnmgr', 'txnmgr_factory'),
('dbt', 'dbt_factory'),
('browserid', 'browserid_factory'),
('auth', 'auth_factory'),
])
def test_sorted_ordering_4(self):
from pyramid.util import FIRST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory', after=FIRST)
add('auth', 'auth_factory', after='browserid')
add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
add('browserid', 'browserid_factory')
add('txnmgr', 'txnmgr_factory', after='exceptionview')
add('dbt', 'dbt_factory')
self.assertEqual(sorter.sorted(),
[
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
('txnmgr', 'txnmgr_factory'),
('browserid', 'browserid_factory'),
('auth', 'auth_factory'),
('dbt', 'dbt_factory'),
])
def test_sorted_ordering_5(self):
from pyramid.util import LAST, FIRST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory')
add('auth', 'auth_factory', after=FIRST)
add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
add('browserid', 'browserid_factory', after=FIRST)
add('txnmgr', 'txnmgr_factory', after='exceptionview', before=LAST)
add('dbt', 'dbt_factory')
self.assertEqual(sorter.sorted(),
[
('browserid', 'browserid_factory'),
('auth', 'auth_factory'),
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
('txnmgr', 'txnmgr_factory'),
('dbt', 'dbt_factory'),
])
def test_sorted_ordering_missing_before_partial(self):
from pyramid.exceptions import ConfigurationError
sorter = self._makeOne()
add = sorter.add
add('dbt', 'dbt_factory')
add('auth', 'auth_factory', after='browserid')
add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
add('browserid', 'browserid_factory')
self.assertRaises(ConfigurationError, sorter.sorted)
def test_sorted_ordering_missing_after_partial(self):
from pyramid.exceptions import ConfigurationError
sorter = self._makeOne()
add = sorter.add
add('dbt', 'dbt_factory')
add('auth', 'auth_factory', after='txnmgr')
add('retry', 'retry_factory', before='dbt', after='exceptionview')
add('browserid', 'browserid_factory')
self.assertRaises(ConfigurationError, sorter.sorted)
def test_sorted_ordering_missing_before_and_after_partials(self):
from pyramid.exceptions import ConfigurationError
sorter = self._makeOne()
add = sorter.add
add('dbt', 'dbt_factory')
add('auth', 'auth_factory', after='browserid')
add('retry', 'retry_factory', before='foo', after='txnmgr')
add('browserid', 'browserid_factory')
self.assertRaises(ConfigurationError, sorter.sorted)
def test_sorted_ordering_missing_before_partial_with_fallback(self):
from pyramid.util import LAST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory', before=LAST)
add('auth', 'auth_factory', after='browserid')
add('retry', 'retry_factory', before=('txnmgr', LAST),
after='exceptionview')
add('browserid', 'browserid_factory')
add('dbt', 'dbt_factory')
self.assertEqual(sorter.sorted(),
[
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
('browserid', 'browserid_factory'),
('auth', 'auth_factory'),
('dbt', 'dbt_factory'),
])
def test_sorted_ordering_missing_after_partial_with_fallback(self):
from pyramid.util import FIRST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory', after=FIRST)
add('auth', 'auth_factory', after=('txnmgr','browserid'))
add('retry', 'retry_factory', after='exceptionview')
add('browserid', 'browserid_factory')
add('dbt', 'dbt_factory')
self.assertEqual(sorter.sorted(),
[
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
('browserid', 'browserid_factory'),
('auth', 'auth_factory'),
('dbt', 'dbt_factory'),
])
def test_sorted_ordering_with_partial_fallbacks(self):
from pyramid.util import LAST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory', before=('wontbethere', LAST))
add('retry', 'retry_factory', after='exceptionview')
add('browserid', 'browserid_factory', before=('wont2', 'exceptionview'))
self.assertEqual(sorter.sorted(),
[
('browserid', 'browserid_factory'),
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
])
def test_sorted_ordering_with_multiple_matching_fallbacks(self):
from pyramid.util import LAST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory', before=LAST)
add('retry', 'retry_factory', after='exceptionview')
add('browserid', 'browserid_factory', before=('retry', 'exceptionview'))
self.assertEqual(sorter.sorted(),
[
('browserid', 'browserid_factory'),
('exceptionview', 'excview_factory'),
('retry', 'retry_factory'),
])
def test_sorted_ordering_with_missing_fallbacks(self):
from pyramid.exceptions import ConfigurationError
from pyramid.util import LAST
sorter = self._makeOne()
add = sorter.add
add('exceptionview', 'excview_factory', before=LAST)
add('retry', 'retry_factory', after='exceptionview')
add('browserid', 'browserid_factory', before=('txnmgr', 'auth'))
self.assertRaises(ConfigurationError, sorter.sorted)
def test_sorted_ordering_conflict_direct(self):
from pyramid.exceptions import CyclicDependencyError
sorter = self._makeOne()
add = sorter.add
add('browserid', 'browserid_factory')
add('auth', 'auth_factory', before='browserid', after='browserid')
self.assertRaises(CyclicDependencyError, sorter.sorted)
def test_sorted_ordering_conflict_indirect(self):
from pyramid.exceptions import CyclicDependencyError
sorter = self._makeOne()
add = sorter.add
add('browserid', 'browserid_factory')
add('auth', 'auth_factory', before='browserid')
add('dbt', 'dbt_factory', after='browserid', before='auth')
self.assertRaises(CyclicDependencyError, sorter.sorted)
class TestSentinel(unittest.TestCase):
def test_repr(self):
from pyramid.util import Sentinel
r = repr(Sentinel('ABC'))
self.assertEqual(r, 'ABC')
class TestActionInfo(unittest.TestCase):
def _getTargetClass(self):
from pyramid.util import ActionInfo
return ActionInfo
def _makeOne(self, filename, lineno, function, linerepr):
return self._getTargetClass()(filename, lineno, function, linerepr)
def test_class_conforms(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import IActionInfo
verifyClass(IActionInfo, self._getTargetClass())
def test_instance_conforms(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import IActionInfo
verifyObject(IActionInfo, self._makeOne('f', 0, 'f', 'f'))
def test_ctor(self):
inst = self._makeOne('filename', 10, 'function', 'src')
self.assertEqual(inst.file, 'filename')
self.assertEqual(inst.line, 10)
self.assertEqual(inst.function, 'function')
self.assertEqual(inst.src, 'src')
def test___str__(self):
inst = self._makeOne('filename', 0, 'function', ' linerepr ')
self.assertEqual(str(inst),
"Line 0 of file filename:\n linerepr ")
def dummyfunc(): pass
class Dummy(object):
pass
pyramid-1.4.5/pyramid/tests/test_settings.py 0000664 0001750 0001750 00000004626 12203712502 020544 0 ustar takaki takaki import unittest
class Test_asbool(unittest.TestCase):
def _callFUT(self, s):
from pyramid.settings import asbool
return asbool(s)
def test_s_is_None(self):
result = self._callFUT(None)
self.assertEqual(result, False)
def test_s_is_True(self):
result = self._callFUT(True)
self.assertEqual(result, True)
def test_s_is_False(self):
result = self._callFUT(False)
self.assertEqual(result, False)
def test_s_is_true(self):
result = self._callFUT('True')
self.assertEqual(result, True)
def test_s_is_false(self):
result = self._callFUT('False')
self.assertEqual(result, False)
def test_s_is_yes(self):
result = self._callFUT('yes')
self.assertEqual(result, True)
def test_s_is_on(self):
result = self._callFUT('on')
self.assertEqual(result, True)
def test_s_is_1(self):
result = self._callFUT(1)
self.assertEqual(result, True)
class Test_aslist_cronly(unittest.TestCase):
def _callFUT(self, val):
from pyramid.settings import aslist_cronly
return aslist_cronly(val)
def test_with_list(self):
result = self._callFUT(['abc', 'def'])
self.assertEqual(result, ['abc', 'def'])
def test_with_string(self):
result = self._callFUT('abc def')
self.assertEqual(result, ['abc def'])
def test_with_string_crsep(self):
result = self._callFUT(' abc\n def')
self.assertEqual(result, ['abc', 'def'])
class Test_aslist(unittest.TestCase):
def _callFUT(self, val, **kw):
from pyramid.settings import aslist
return aslist(val, **kw)
def test_with_list(self):
result = self._callFUT(['abc', 'def'])
self.assertEqual(list(result), ['abc', 'def'])
def test_with_string(self):
result = self._callFUT('abc def')
self.assertEqual(result, ['abc', 'def'])
def test_with_string_crsep(self):
result = self._callFUT(' abc\n def')
self.assertEqual(result, ['abc', 'def'])
def test_with_string_crsep_spacesep(self):
result = self._callFUT(' abc\n def ghi')
self.assertEqual(result, ['abc', 'def', 'ghi'])
def test_with_string_crsep_spacesep_no_flatten(self):
result = self._callFUT(' abc\n def ghi ', flatten=False)
self.assertEqual(result, ['abc', 'def ghi'])
pyramid-1.4.5/pyramid/tests/test_scripts/ 0000775 0001750 0001750 00000000000 12210157153 020014 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/test_scripts/test_pshell.py 0000664 0001750 0001750 00000033650 12203712502 022720 0 ustar takaki takaki import unittest
from pyramid.tests.test_scripts import dummy
class TestPShellCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.pshell import PShellCommand
return PShellCommand
def _makeOne(self, patch_bootstrap=True, patch_config=True,
patch_args=True, patch_options=True):
cmd = self._getTargetClass()([])
if patch_bootstrap:
self.bootstrap = dummy.DummyBootstrap()
cmd.bootstrap = (self.bootstrap,)
if patch_config:
self.config_factory = dummy.DummyConfigParserFactory()
cmd.ConfigParser = self.config_factory
if patch_args:
self.args = ('/foo/bar/myapp.ini#myapp',)
cmd.args = self.args
if patch_options:
class Options(object): pass
self.options = Options()
self.options.python_shell = ''
self.options.setup = None
cmd.options = self.options
return cmd
def test_make_default_shell(self):
command = self._makeOne()
interact = dummy.DummyInteractor()
shell = command.make_default_shell(interact)
shell({'foo': 'bar'}, 'a help message')
self.assertEqual(interact.local, {'foo': 'bar'})
self.assertTrue('a help message' in interact.banner)
def test_make_bpython_shell(self):
command = self._makeOne()
bpython = dummy.DummyBPythonShell()
shell = command.make_bpython_shell(bpython)
shell({'foo': 'bar'}, 'a help message')
self.assertEqual(bpython.locals_, {'foo': 'bar'})
self.assertTrue('a help message' in bpython.banner)
def test_make_ipython_v0_11_shell(self):
command = self._makeOne()
ipshell_factory = dummy.DummyIPShellFactory()
shell = command.make_ipython_v0_11_shell(ipshell_factory)
shell({'foo': 'bar'}, 'a help message')
self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
self.assertTrue('a help message' in ipshell_factory.kw['banner2'])
self.assertTrue(ipshell_factory.shell.called)
def test_make_ipython_v0_10_shell(self):
command = self._makeOne()
ipshell_factory = dummy.DummyIPShellFactory()
shell = command.make_ipython_v0_10_shell(ipshell_factory)
shell({'foo': 'bar'}, 'a help message')
self.assertEqual(ipshell_factory.kw['argv'], [])
self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
self.assertTrue('a help message' in ipshell_factory.shell.banner)
self.assertTrue(ipshell_factory.shell.called)
def test_command_loads_default_shell(self):
command = self._makeOne()
shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: None
command.make_ipython_v0_10_shell = lambda: None
command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: shell
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':self.bootstrap.root,
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_loads_default_shell_with_unknown_shell(self):
command = self._makeOne()
shell = dummy.DummyShell()
bad_shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: bad_shell
command.make_ipython_v0_10_shell = lambda: bad_shell
command.make_bpython_shell = lambda: bad_shell
command.make_default_shell = lambda: shell
command.options.python_shell = 'unknow_python_shell'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':self.bootstrap.root,
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
})
self.assertEqual(bad_shell.env, {})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_loads_ipython_v0_11(self):
command = self._makeOne()
shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: shell
command.make_ipython_v0_10_shell = lambda: None
command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: None
command.options.python_shell = 'ipython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':self.bootstrap.root,
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_loads_ipython_v0_10(self):
command = self._makeOne()
shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: None
command.make_ipython_v0_10_shell = lambda: shell
command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: None
command.options.python_shell = 'ipython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':self.bootstrap.root,
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_loads_bpython_shell(self):
command = self._makeOne()
shell = dummy.DummyBPythonShell()
command.make_ipython_v0_11_shell = lambda: None
command.make_ipython_v0_10_shell = lambda: None
command.make_bpython_shell = lambda: shell
command.options.python_shell = 'bpython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.locals_, {
'app':self.bootstrap.app, 'root':self.bootstrap.root,
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.banner)
def test_shell_ipython_ordering(self):
command = self._makeOne()
shell0_11 = dummy.DummyShell()
shell0_10 = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: shell0_11
command.make_ipython_v0_10_shell = lambda: shell0_10
command.make_bpython_shell = lambda: None
shell = command.make_shell()
self.assertEqual(shell, shell0_11)
command.options.python_shell = 'ipython'
shell = command.make_shell()
self.assertEqual(shell, shell0_11)
def test_shell_ordering(self):
command = self._makeOne()
ipshell = dummy.DummyShell()
bpshell = dummy.DummyShell()
dshell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: None
command.make_ipython_v0_10_shell = lambda: None
command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: dshell
shell = command.make_shell()
self.assertEqual(shell, dshell)
command.options.python_shell = 'ipython'
shell = command.make_shell()
self.assertEqual(shell, dshell)
command.options.python_shell = 'bpython'
shell = command.make_shell()
self.assertEqual(shell, dshell)
command.make_ipython_v0_11_shell = lambda: ipshell
command.make_bpython_shell = lambda: bpshell
command.options.python_shell = 'ipython'
shell = command.make_shell()
self.assertEqual(shell, ipshell)
command.options.python_shell = 'bpython'
shell = command.make_shell()
self.assertEqual(shell, bpshell)
command.options.python_shell = 'python'
shell = command.make_shell()
self.assertEqual(shell, dshell)
def test_command_loads_custom_items(self):
command = self._makeOne()
model = dummy.Dummy()
self.config_factory.items = [('m', model)]
shell = dummy.DummyShell()
command.run(shell)
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':self.bootstrap.root,
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
'm':model,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_setup(self):
command = self._makeOne()
def setup(env):
env['a'] = 1
env['root'] = 'root override'
self.config_factory.items = [('setup', setup)]
shell = dummy.DummyShell()
command.run(shell)
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':'root override',
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
'a':1,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_loads_check_variable_override_order(self):
command = self._makeOne()
model = dummy.Dummy()
def setup(env):
env['a'] = 1
env['m'] = 'model override'
env['root'] = 'root override'
self.config_factory.items = [('setup', setup), ('m', model)]
shell = dummy.DummyShell()
command.run(shell)
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':'root override',
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
'a':1, 'm':model,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_loads_setup_from_options(self):
command = self._makeOne()
def setup(env):
env['a'] = 1
env['root'] = 'root override'
model = dummy.Dummy()
self.config_factory.items = [('setup', 'abc'),
('m', model)]
command.options.setup = setup
shell = dummy.DummyShell()
command.run(shell)
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':self.bootstrap.app, 'root':'root override',
'registry':self.bootstrap.registry,
'request':self.bootstrap.request,
'root_factory':self.bootstrap.root_factory,
'a':1, 'm':model,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
def test_command_custom_section_override(self):
command = self._makeOne()
dummy_ = dummy.Dummy()
self.config_factory.items = [('app', dummy_), ('root', dummy_),
('registry', dummy_), ('request', dummy_)]
shell = dummy.DummyShell()
command.run(shell)
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
'/foo/bar/myapp.ini')
self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
self.assertEqual(shell.env, {
'app':dummy_, 'root':dummy_, 'registry':dummy_, 'request':dummy_,
'root_factory':self.bootstrap.root_factory,
})
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.pshell import main
return main(argv, quiet=True)
def test_it(self):
result = self._callFUT(['pshell'])
self.assertEqual(result, 2)
pyramid-1.4.5/pyramid/tests/test_scripts/test_proutes.py 0000664 0001750 0001750 00000014317 12203712502 023131 0 ustar takaki takaki import unittest
from pyramid.tests.test_scripts import dummy
class TestPRoutesCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.proutes import PRoutesCommand
return PRoutesCommand
def _makeOne(self):
cmd = self._getTargetClass()([])
cmd.bootstrap = (dummy.DummyBootstrap(),)
cmd.args = ('/foo/bar/myapp.ini#myapp',)
return cmd
def test_good_args(self):
cmd = self._getTargetClass()([])
cmd.bootstrap = (dummy.DummyBootstrap(),)
cmd.args = ('/foo/bar/myapp.ini#myapp', 'a=1')
route = dummy.DummyRoute('a', '/a')
mapper = dummy.DummyMapper(route)
cmd._get_mapper = lambda *arg: mapper
L = []
cmd.out = lambda msg: L.append(msg)
cmd.run()
self.assertTrue('' in ''.join(L))
def test_bad_args(self):
cmd = self._getTargetClass()([])
cmd.bootstrap = (dummy.DummyBootstrap(),)
cmd.args = ('/foo/bar/myapp.ini#myapp', 'a')
route = dummy.DummyRoute('a', '/a')
mapper = dummy.DummyMapper(route)
cmd._get_mapper = lambda *arg: mapper
self.assertRaises(ValueError, cmd.run)
def test_no_routes(self):
command = self._makeOne()
mapper = dummy.DummyMapper()
command._get_mapper = lambda *arg: mapper
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L, [])
def test_no_mapper(self):
command = self._makeOne()
command._get_mapper = lambda *arg:None
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L, [])
def test_single_route_no_route_registered(self):
command = self._makeOne()
route = dummy.DummyRoute('a', '/a')
mapper = dummy.DummyMapper(route)
command._get_mapper = lambda *arg: mapper
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(len(L), 3)
self.assertEqual(L[-1].split(), ['a', '/a', ''])
def test_route_with_no_slash_prefix(self):
command = self._makeOne()
route = dummy.DummyRoute('a', 'a')
mapper = dummy.DummyMapper(route)
command._get_mapper = lambda *arg: mapper
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(len(L), 3)
self.assertEqual(L[-1].split(), ['a', '/a', ''])
def test_single_route_no_views_registered(self):
from zope.interface import Interface
from pyramid.registry import Registry
from pyramid.interfaces import IRouteRequest
registry = Registry()
def view():pass
class IMyRoute(Interface):
pass
registry.registerUtility(IMyRoute, IRouteRequest, name='a')
command = self._makeOne()
route = dummy.DummyRoute('a', '/a')
mapper = dummy.DummyMapper(route)
command._get_mapper = lambda *arg: mapper
L = []
command.out = L.append
command.bootstrap = (dummy.DummyBootstrap(registry=registry),)
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(len(L), 3)
self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None'])
def test_single_route_one_view_registered(self):
from zope.interface import Interface
from pyramid.registry import Registry
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
registry = Registry()
def view():pass
class IMyRoute(Interface):
pass
registry.registerAdapter(view,
(IViewClassifier, IMyRoute, Interface),
IView, '')
registry.registerUtility(IMyRoute, IRouteRequest, name='a')
command = self._makeOne()
route = dummy.DummyRoute('a', '/a')
mapper = dummy.DummyMapper(route)
command._get_mapper = lambda *arg: mapper
L = []
command.out = L.append
command.bootstrap = (dummy.DummyBootstrap(registry=registry),)
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(len(L), 3)
compare_to = L[-1].split()[:3]
self.assertEqual(compare_to, ['a', '/a', ''])
def test__get_mapper(self):
from pyramid.registry import Registry
from pyramid.urldispatch import RoutesMapper
command = self._makeOne()
registry = Registry()
result = command._get_mapper(registry)
self.assertEqual(result.__class__, RoutesMapper)
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.proutes import main
return main(argv, quiet=True)
def test_it(self):
result = self._callFUT(['proutes'])
self.assertEqual(result, 2)
pyramid-1.4.5/pyramid/tests/test_scripts/test_common.py 0000664 0001750 0001750 00000002450 12203712502 022713 0 ustar takaki takaki import os
import unittest
class Test_logging_file_config(unittest.TestCase):
def _callFUT(self, config_file):
from pyramid.scripts.common import logging_file_config
dummy_cp = DummyConfigParserModule
return logging_file_config(config_file, self.fileConfig, dummy_cp)
def test_it(self):
config_file, dict = self._callFUT('/abc')
# use of os.path.abspath here is a sop to Windows
self.assertEqual(config_file, os.path.abspath('/abc'))
self.assertEqual(dict['__file__'], os.path.abspath('/abc'))
self.assertEqual(dict['here'], os.path.abspath('/'))
def fileConfig(self, config_file, dict):
return config_file, dict
class TestParseVars(unittest.TestCase):
def test_parse_vars_good(self):
from pyramid.scripts.common import parse_vars
vars = ['a=1', 'b=2']
result = parse_vars(vars)
self.assertEqual(result, {'a': '1', 'b': '2'})
def test_parse_vars_bad(self):
from pyramid.scripts.common import parse_vars
vars = ['a']
self.assertRaises(ValueError, parse_vars, vars)
class DummyConfigParser(object):
def read(self, x):
pass
def has_section(self, name):
return True
class DummyConfigParserModule(object):
ConfigParser = DummyConfigParser
pyramid-1.4.5/pyramid/tests/test_scripts/test_pcreate.py 0000664 0001750 0001750 00000010312 12203712502 023042 0 ustar takaki takaki import unittest
class TestPCreateCommand(unittest.TestCase):
def setUp(self):
from pyramid.compat import NativeIO
self.out_ = NativeIO()
def out(self, msg):
self.out_.write(msg)
def _getTargetClass(self):
from pyramid.scripts.pcreate import PCreateCommand
return PCreateCommand
def _makeOne(self, *args):
effargs = ['pcreate']
effargs.extend(args)
cmd = self._getTargetClass()(effargs)
cmd.out = self.out
return cmd
def test_run_show_scaffolds_exist(self):
cmd = self._makeOne('-l')
result = cmd.run()
self.assertEqual(result, 0)
out = self.out_.getvalue()
self.assertTrue(out.startswith('Available scaffolds'))
def test_run_show_scaffolds_none_exist(self):
cmd = self._makeOne('-l')
cmd.scaffolds = []
result = cmd.run()
self.assertEqual(result, 0)
out = self.out_.getvalue()
self.assertTrue(out.startswith('No scaffolds available'))
def test_run_no_scaffold_name(self):
cmd = self._makeOne()
result = cmd.run()
self.assertEqual(result, 2)
out = self.out_.getvalue()
self.assertTrue(out.startswith(
'You must provide at least one scaffold name'))
def test_no_project_name(self):
cmd = self._makeOne('-s', 'dummy')
result = cmd.run()
self.assertEqual(result, 2)
out = self.out_.getvalue()
self.assertTrue(out.startswith('You must provide a project name'))
def test_unknown_scaffold_name(self):
cmd = self._makeOne('-s', 'dummyXX', 'distro')
result = cmd.run()
self.assertEqual(result, 2)
out = self.out_.getvalue()
self.assertTrue(out.startswith('Unavailable scaffolds'))
def test_known_scaffold_single_rendered(self):
import os
cmd = self._makeOne('-s', 'dummy', 'Distro')
scaffold = DummyScaffold('dummy')
cmd.scaffolds = [scaffold]
result = cmd.run()
self.assertEqual(result, 0)
self.assertEqual(
scaffold.output_dir,
os.path.normpath(os.path.join(os.getcwd(), 'Distro'))
)
self.assertEqual(
scaffold.vars,
{'project': 'Distro', 'egg': 'Distro', 'package': 'distro'})
def test_known_scaffold_absolute_path(self):
import os
path = os.path.abspath('Distro')
cmd = self._makeOne('-s', 'dummy', path)
scaffold = DummyScaffold('dummy')
cmd.scaffolds = [scaffold]
result = cmd.run()
self.assertEqual(result, 0)
self.assertEqual(
scaffold.output_dir,
os.path.normpath(os.path.join(os.getcwd(), 'Distro'))
)
self.assertEqual(
scaffold.vars,
{'project': 'Distro', 'egg': 'Distro', 'package': 'distro'})
def test_known_scaffold_multiple_rendered(self):
import os
cmd = self._makeOne('-s', 'dummy1', '-s', 'dummy2', 'Distro')
scaffold1 = DummyScaffold('dummy1')
scaffold2 = DummyScaffold('dummy2')
cmd.scaffolds = [scaffold1, scaffold2]
result = cmd.run()
self.assertEqual(result, 0)
self.assertEqual(
scaffold1.output_dir,
os.path.normpath(os.path.join(os.getcwd(), 'Distro'))
)
self.assertEqual(
scaffold1.vars,
{'project': 'Distro', 'egg': 'Distro', 'package': 'distro'})
self.assertEqual(
scaffold2.output_dir,
os.path.normpath(os.path.join(os.getcwd(), 'Distro'))
)
self.assertEqual(
scaffold2.vars,
{'project': 'Distro', 'egg': 'Distro', 'package': 'distro'})
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.pcreate import main
return main(argv, quiet=True)
def test_it(self):
result = self._callFUT(['pcreate'])
self.assertEqual(result, 2)
class DummyScaffold(object):
def __init__(self, name):
self.name = name
def run(self, command, output_dir, vars):
self.command = command
self.output_dir = output_dir
self.vars = vars
pyramid-1.4.5/pyramid/tests/test_scripts/dummy.py 0000664 0001750 0001750 00000007625 12203712502 021530 0 ustar takaki takaki class DummyTweens(object):
def __init__(self, implicit, explicit):
self._implicit = implicit
self.explicit = explicit
self.name_to_alias = {}
def implicit(self):
return self._implicit
class Dummy:
pass
dummy_root = Dummy()
class DummyRegistry(object):
settings = {}
def queryUtility(self, iface, default=None, name=''):
return default
dummy_registry = DummyRegistry()
class DummyShell(object):
env = {}
help = ''
def __call__(self, env, help):
self.env = env
self.help = help
class DummyInteractor:
def __call__(self, banner, local):
self.banner = banner
self.local = local
class DummyBPythonShell:
def __call__(self, locals_, banner):
self.locals_ = locals_
self.banner = banner
class DummyIPShell(object):
IP = Dummy()
IP.BANNER = 'foo'
def set_banner(self, banner):
self.banner = banner
def __call__(self):
self.called = True
class DummyIPShellFactory(object):
def __call__(self, **kw):
self.kw = kw
self.shell = DummyIPShell()
return self.shell
class DummyApp:
def __init__(self):
self.registry = dummy_registry
class DummyMapper(object):
def __init__(self, *routes):
self.routes = routes
def get_routes(self):
return self.routes
class DummyRoute(object):
def __init__(self, name, pattern, factory=None,
matchdict=None, predicate=None):
self.name = name
self.path = pattern
self.pattern = pattern
self.factory = factory
self.matchdict = matchdict
self.predicates = []
if predicate is not None:
self.predicates = [predicate]
def match(self, route):
return self.matchdict
class DummyRequest:
application_url = 'http://example.com:5432'
script_name = ''
def __init__(self, environ):
self.environ = environ
self.matchdict = {}
class DummyView(object):
def __init__(self, **attrs):
self.__request_attrs__ = attrs
from zope.interface import implementer
from pyramid.interfaces import IMultiView
@implementer(IMultiView)
class DummyMultiView(object):
def __init__(self, *views, **attrs):
self.views = [(None, view, None) for view in views]
self.__request_attrs__ = attrs
class DummyConfigParser(object):
def __init__(self, result):
self.result = result
def read(self, filename):
self.filename = filename
def items(self, section):
self.section = section
if self.result is None:
from pyramid.compat import configparser
raise configparser.NoSectionError(section)
return self.result
class DummyConfigParserFactory(object):
items = None
def __call__(self):
self.parser = DummyConfigParser(self.items)
return self.parser
class DummyCloser(object):
def __call__(self):
self.called = True
class DummyBootstrap(object):
def __init__(self, app=None, registry=None, request=None, root=None,
root_factory=None, closer=None):
self.app = app or DummyApp()
if registry is None:
registry = DummyRegistry()
self.registry = registry
if request is None:
request = DummyRequest({})
self.request = request
if root is None:
root = Dummy()
self.root = root
if root_factory is None:
root_factory = Dummy()
self.root_factory = root_factory
if closer is None:
closer = DummyCloser()
self.closer = closer
def __call__(self, *a, **kw):
self.a = a
self.kw = kw
return {
'app': self.app,
'registry': self.registry,
'request': self.request,
'root': self.root,
'root_factory': self.root_factory,
'closer': self.closer,
}
pyramid-1.4.5/pyramid/tests/test_scripts/test_prequest.py 0000664 0001750 0001750 00000013404 12210154276 023302 0 ustar takaki takaki import unittest
class TestPRequestCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.prequest import PRequestCommand
return PRequestCommand
def _makeOne(self, argv, headers=None):
cmd = self._getTargetClass()(argv)
cmd.get_app = self.get_app
self._headers = headers or []
self._out = []
cmd.out = self.out
return cmd
def get_app(self, spec, app_name=None, options=None):
self._spec = spec
self._app_name = app_name
self._options = options or {}
def helloworld(environ, start_request):
self._environ = environ
self._path_info = environ['PATH_INFO']
start_request('200 OK', self._headers)
return [b'abc']
return helloworld
def out(self, msg):
self._out.append(msg)
def test_command_not_enough_args(self):
command = self._makeOne([])
command.run()
self.assertEqual(self._out, ['You must provide at least two arguments'])
def test_command_two_args(self):
command = self._makeOne(['', 'development.ini', '/'])
command.run()
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_path_doesnt_start_with_slash(self):
command = self._makeOne(['', 'development.ini', 'abc'])
command.run()
self.assertEqual(self._path_info, '/abc')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_has_bad_config_header(self):
command = self._makeOne(
['', '--header=name','development.ini', '/'])
command.run()
self.assertEqual(
self._out[0],
("Bad --header=name option, value must be in the form "
"'name:value'"))
def test_command_has_good_header_var(self):
command = self._makeOne(
['', '--header=name:value','development.ini', '/'])
command.run()
self.assertEqual(self._environ['HTTP_NAME'], 'value')
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_has_content_type_header_var(self):
command = self._makeOne(
['', '--header=content-type:app/foo','development.ini', '/'])
command.run()
self.assertEqual(self._environ['CONTENT_TYPE'], 'app/foo')
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_has_multiple_header_vars(self):
command = self._makeOne(
['',
'--header=name:value',
'--header=name2:value2',
'development.ini',
'/'])
command.run()
self.assertEqual(self._environ['HTTP_NAME'], 'value')
self.assertEqual(self._environ['HTTP_NAME2'], 'value2')
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_method_get(self):
command = self._makeOne(['', '--method=GET', 'development.ini', '/'])
command.run()
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_method_post(self):
from pyramid.compat import NativeIO
command = self._makeOne(['', '--method=POST', 'development.ini', '/'])
stdin = NativeIO()
command.stdin = stdin
command.run()
self.assertEqual(self._environ['CONTENT_LENGTH'], '-1')
self.assertEqual(self._environ['wsgi.input'], stdin)
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_with_query_string(self):
command = self._makeOne(['', 'development.ini', '/abc?a=1&b=2&c'])
command.run()
self.assertEqual(self._environ['QUERY_STRING'], 'a=1&b=2&c')
self.assertEqual(self._path_info, '/abc')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, ['abc'])
def test_command_display_headers(self):
command = self._makeOne(
['', '--display-headers', 'development.ini', '/'])
command.run()
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(
self._out,
['200 OK', 'Content-Type: text/html; charset=UTF-8', 'abc'])
def test_command_response_has_no_charset(self):
command = self._makeOne(['', '--method=GET', 'development.ini', '/'],
headers=[('Content-Type', 'image/jpeg')])
command.run()
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
self.assertEqual(self._out, [b'abc'])
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.prequest import main
return main(argv, True)
def test_it(self):
result = self._callFUT(['prequest'])
self.assertEqual(result, 2)
pyramid-1.4.5/pyramid/tests/test_scripts/test_pserve.py 0000664 0001750 0001750 00000025651 12210154276 022745 0 ustar takaki takaki import atexit
import os
import tempfile
import unittest
from pyramid.compat import PY3
if PY3: # pragma: no cover
import builtins as __builtin__
else:
import __builtin__
class TestPServeCommand(unittest.TestCase):
def setUp(self):
from pyramid.compat import NativeIO
self.out_ = NativeIO()
self.pid_file = None
def tearDown(self):
if self.pid_file and os.path.exists(self.pid_file):
os.remove(self.pid_file)
def out(self, msg):
self.out_.write(msg)
def _get_server(*args, **kwargs):
def server(app):
return ''
return server
def _getTargetClass(self):
from pyramid.scripts.pserve import PServeCommand
return PServeCommand
def _makeOne(self, *args):
effargs = ['pserve']
effargs.extend(args)
cmd = self._getTargetClass()(effargs)
cmd.out = self.out
return cmd
def _makeOneWithPidFile(self, pid):
self.pid_file = tempfile.mktemp()
inst = self._makeOne()
with open(self.pid_file, 'w') as f:
f.write(str(pid))
return inst
def test_remove_pid_file_verbose(self):
inst = self._makeOneWithPidFile(os.getpid())
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=1)
self._assert_pid_file_removed(verbose=True)
def test_remove_pid_file_not_verbose(self):
inst = self._makeOneWithPidFile(os.getpid())
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=0)
self._assert_pid_file_removed(verbose=False)
def test_remove_pid_not_a_number(self):
inst = self._makeOneWithPidFile('not a number')
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=1)
self._assert_pid_file_removed(verbose=True)
def test_remove_pid_current_pid_is_not_written_pid(self):
inst = self._makeOneWithPidFile(os.getpid())
inst._remove_pid_file('99999', self.pid_file, verbosity=1)
self._assert_pid_file_not_removed('')
def test_remove_pid_current_pid_is_not_pid_in_file(self):
inst = self._makeOneWithPidFile('99999')
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=1)
msg = 'PID file %s contains 99999, not expected PID %s'
self._assert_pid_file_not_removed(msg % (self.pid_file, os.getpid()))
def test_remove_pid_no_pid_file(self):
inst = self._makeOne()
self.pid_file = 'some unknown path'
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=1)
self._assert_pid_file_removed(verbose=False)
def test_remove_pid_file_unlink_exception(self):
inst = self._makeOneWithPidFile(os.getpid())
self._remove_pid_unlink_exception(inst)
msg = [
'Removing PID file %s' % (self.pid_file),
'Cannot remove PID file: (Some OSError - unlink)',
'Stale PID removed']
self._assert_pid_file_not_removed(msg=''.join(msg))
with open(self.pid_file) as f:
self.assertEqual(f.read(), '')
def test_remove_pid_file_stale_pid_write_exception(self):
inst = self._makeOneWithPidFile(os.getpid())
self._remove_pid_unlink_and_write_exceptions(inst)
msg = [
'Removing PID file %s' % (self.pid_file),
'Cannot remove PID file: (Some OSError - unlink)',
'Stale PID left in file: %s ' % (self.pid_file),
'(Some OSError - open)']
self._assert_pid_file_not_removed(msg=''.join(msg))
with open(self.pid_file) as f:
self.assertEqual(int(f.read()), os.getpid())
def test_record_pid_verbose(self):
self._assert_record_pid(verbosity=2, msg='Writing PID %d to %s')
def test_record_pid_not_verbose(self):
self._assert_record_pid(verbosity=1, msg='')
def _remove_pid_unlink_exception(self, inst):
old_unlink = os.unlink
def fake_unlink(filename):
raise OSError('Some OSError - unlink')
try:
os.unlink = fake_unlink
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=1)
finally:
os.unlink = old_unlink
def _remove_pid_unlink_and_write_exceptions(self, inst):
old_unlink = os.unlink
def fake_unlink(filename):
raise OSError('Some OSError - unlink')
run_already = []
old_open = __builtin__.open
def fake_open(*args):
if not run_already:
run_already.append(True)
return old_open(*args)
raise OSError('Some OSError - open')
try:
os.unlink = fake_unlink
__builtin__.open = fake_open
inst._remove_pid_file(os.getpid(), self.pid_file, verbosity=1)
finally:
os.unlink = old_unlink
__builtin__.open = old_open
def _assert_pid_file_removed(self, verbose=False):
self.assertFalse(os.path.exists(self.pid_file))
msg = 'Removing PID file %s' % (self.pid_file) if verbose else ''
self.assertEqual(self.out_.getvalue(), msg)
def _assert_pid_file_not_removed(self, msg):
self.assertTrue(os.path.exists(self.pid_file))
self.assertEqual(self.out_.getvalue(), msg)
def _assert_record_pid(self, verbosity, msg):
old_atexit = atexit.register
def fake_atexit(*args):
pass
self.pid_file = tempfile.mktemp()
pid = os.getpid()
inst = self._makeOne()
inst.verbose = verbosity
try:
atexit.register = fake_atexit
inst.record_pid(self.pid_file)
finally:
atexit.register = old_atexit
msg = msg % (pid, self.pid_file) if msg else ''
self.assertEqual(self.out_.getvalue(), msg)
with open(self.pid_file) as f:
self.assertEqual(int(f.read()), pid)
def test_run_no_args(self):
inst = self._makeOne()
result = inst.run()
self.assertEqual(result, 2)
self.assertEqual(self.out_.getvalue(), 'You must give a config file')
def test_run_stop_daemon_no_such_pid_file(self):
path = os.path.join(os.path.dirname(__file__), 'wontexist.pid')
inst = self._makeOne('--stop-daemon', '--pid-file=%s' % path)
inst.run()
msg = 'No PID file exists in %s' % path
self.assertEqual(self.out_.getvalue(), msg)
def test_run_stop_daemon_bad_pid_file(self):
path = __file__
inst = self._makeOne('--stop-daemon', '--pid-file=%s' % path)
inst.run()
msg = 'Not a valid PID file in %s' % path
self.assertEqual(self.out_.getvalue(), msg)
def test_run_stop_daemon_invalid_pid_in_file(self):
fn = tempfile.mktemp()
with open(fn, 'wb') as tmp:
tmp.write(b'9999999')
tmp.close()
inst = self._makeOne('--stop-daemon', '--pid-file=%s' % fn)
inst.run()
msg = 'PID in %s is not valid (deleting)' % fn
self.assertEqual(self.out_.getvalue(), msg)
def test_get_options_with_command(self):
inst = self._makeOne()
inst.args = ['foo', 'stop', 'a=1', 'b=2']
result = inst.get_options()
self.assertEqual(result, {'a': '1', 'b': '2'})
def test_get_options_no_command(self):
inst = self._makeOne()
inst.args = ['foo', 'a=1', 'b=2']
result = inst.get_options()
self.assertEqual(result, {'a': '1', 'b': '2'})
def test_parse_vars_good(self):
from pyramid.tests.test_scripts.dummy import DummyApp
inst = self._makeOne('development.ini', 'a=1', 'b=2')
inst.loadserver = self._get_server
app = DummyApp()
def get_app(*args, **kwargs):
app.global_conf = kwargs.get('global_conf', None)
inst.loadapp = get_app
inst.run()
self.assertEqual(app.global_conf, {'a': '1', 'b': '2'})
def test_parse_vars_bad(self):
inst = self._makeOne('development.ini', 'a')
inst.loadserver = self._get_server
self.assertRaises(ValueError, inst.run)
class Test_read_pidfile(unittest.TestCase):
def _callFUT(self, filename):
from pyramid.scripts.pserve import read_pidfile
return read_pidfile(filename)
def test_read_pidfile(self):
filename = tempfile.mktemp()
try:
with open(filename, 'w') as f:
f.write('12345')
result = self._callFUT(filename)
self.assertEqual(result, 12345)
finally:
os.remove(filename)
def test_read_pidfile_no_pid_file(self):
result = self._callFUT('some unknown path')
self.assertEqual(result, None)
def test_read_pidfile_not_a_number(self):
result = self._callFUT(__file__)
self.assertEqual(result, None)
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.pserve import main
return main(argv, quiet=True)
def test_it(self):
result = self._callFUT(['pserve'])
self.assertEqual(result, 2)
class TestLazyWriter(unittest.TestCase):
def _makeOne(self, filename, mode='w'):
from pyramid.scripts.pserve import LazyWriter
return LazyWriter(filename, mode)
def test_open(self):
filename = tempfile.mktemp()
try:
inst = self._makeOne(filename)
fp = inst.open()
self.assertEqual(fp.name, filename)
finally:
fp.close()
os.remove(filename)
def test_write(self):
filename = tempfile.mktemp()
try:
inst = self._makeOne(filename)
inst.write('hello')
finally:
with open(filename) as f:
data = f.read()
self.assertEqual(data, 'hello')
inst.close()
os.remove(filename)
def test_writeline(self):
filename = tempfile.mktemp()
try:
inst = self._makeOne(filename)
inst.writelines('hello')
finally:
with open(filename) as f:
data = f.read()
self.assertEqual(data, 'hello')
inst.close()
os.remove(filename)
def test_flush(self):
filename = tempfile.mktemp()
try:
inst = self._makeOne(filename)
inst.flush()
fp = inst.fileobj
self.assertEqual(fp.name, filename)
finally:
fp.close()
os.remove(filename)
class Test__methodwrapper(unittest.TestCase):
def _makeOne(self, func, obj, type):
from pyramid.scripts.pserve import _methodwrapper
return _methodwrapper(func, obj, type)
def test___call__succeed(self):
def foo(self, cls, a=1): return 1
class Bar(object): pass
wrapper = self._makeOne(foo, Bar, None)
result = wrapper(a=1)
self.assertEqual(result, 1)
def test___call__fail(self):
def foo(self, cls, a=1): return 1
class Bar(object): pass
wrapper = self._makeOne(foo, Bar, None)
self.assertRaises(AssertionError, wrapper, cls=1)
pyramid-1.4.5/pyramid/tests/test_scripts/__init__.py 0000664 0001750 0001750 00000000012 12203712502 022113 0 ustar takaki takaki # package
pyramid-1.4.5/pyramid/tests/test_scripts/test_ptweens.py 0000664 0001750 0001750 00000003734 12203712502 023116 0 ustar takaki takaki import unittest
from pyramid.tests.test_scripts import dummy
class TestPTweensCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.ptweens import PTweensCommand
return PTweensCommand
def _makeOne(self):
cmd = self._getTargetClass()([])
cmd.bootstrap = (dummy.DummyBootstrap(),)
cmd.args = ('/foo/bar/myapp.ini#myapp',)
return cmd
def test_command_no_tweens(self):
command = self._makeOne()
command._get_tweens = lambda *arg: None
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L, [])
def test_command_implicit_tweens_only(self):
command = self._makeOne()
tweens = dummy.DummyTweens([('name', 'item')], None)
command._get_tweens = lambda *arg: tweens
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(
L[0],
'"pyramid.tweens" config value NOT set (implicitly ordered tweens '
'used)')
def test_command_implicit_and_explicit_tweens(self):
command = self._makeOne()
tweens = dummy.DummyTweens([('name', 'item')], [('name2', 'item2')])
command._get_tweens = lambda *arg: tweens
L = []
command.out = L.append
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(
L[0],
'"pyramid.tweens" config value set (explicitly ordered tweens used)')
def test__get_tweens(self):
command = self._makeOne()
registry = dummy.DummyRegistry()
self.assertEqual(command._get_tweens(registry), None)
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.ptweens import main
return main(argv, quiet=True)
def test_it(self):
result = self._callFUT(['ptweens'])
self.assertEqual(result, 2)
pyramid-1.4.5/pyramid/tests/test_scripts/test_pviews.py 0000664 0001750 0001750 00000050140 12203712502 022737 0 ustar takaki takaki import unittest
from pyramid.tests.test_scripts import dummy
class TestPViewsCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scripts.pviews import PViewsCommand
return PViewsCommand
def _makeOne(self, registry=None):
cmd = self._getTargetClass()([])
cmd.bootstrap = (dummy.DummyBootstrap(registry=registry),)
cmd.args = ('/foo/bar/myapp.ini#myapp',)
return cmd
def _register_mapper(self, registry, routes):
from pyramid.interfaces import IRoutesMapper
mapper = dummy.DummyMapper(*routes)
registry.registerUtility(mapper, IRoutesMapper)
def test__find_view_no_match(self):
from pyramid.registry import Registry
registry = Registry()
self._register_mapper(registry, [])
command = self._makeOne(registry)
result = command._find_view('/a', registry)
self.assertEqual(result, None)
def test__find_view_no_match_multiview_registered(self):
from zope.interface import implementer
from zope.interface import providedBy
from pyramid.interfaces import IRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IMultiView
from pyramid.traversal import DefaultRootFactory
from pyramid.registry import Registry
registry = Registry()
@implementer(IMultiView)
class View1(object):
pass
request = dummy.DummyRequest({'PATH_INFO':'/a'})
root = DefaultRootFactory(request)
root_iface = providedBy(root)
registry.registerAdapter(View1(),
(IViewClassifier, IRequest, root_iface),
IMultiView)
self._register_mapper(registry, [])
command = self._makeOne(registry=registry)
result = command._find_view('/x', registry)
self.assertEqual(result, None)
def test__find_view_traversal(self):
from zope.interface import providedBy
from pyramid.interfaces import IRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
from pyramid.traversal import DefaultRootFactory
from pyramid.registry import Registry
registry = Registry()
def view1(): pass
request = dummy.DummyRequest({'PATH_INFO':'/a'})
root = DefaultRootFactory(request)
root_iface = providedBy(root)
registry.registerAdapter(view1,
(IViewClassifier, IRequest, root_iface),
IView, name='a')
self._register_mapper(registry, [])
command = self._makeOne(registry=registry)
result = command._find_view('/a', registry)
self.assertEqual(result, view1)
def test__find_view_traversal_multiview(self):
from zope.interface import implementer
from zope.interface import providedBy
from pyramid.interfaces import IRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IMultiView
from pyramid.traversal import DefaultRootFactory
from pyramid.registry import Registry
registry = Registry()
@implementer(IMultiView)
class View1(object):
pass
request = dummy.DummyRequest({'PATH_INFO':'/a'})
root = DefaultRootFactory(request)
root_iface = providedBy(root)
view = View1()
registry.registerAdapter(view,
(IViewClassifier, IRequest, root_iface),
IMultiView, name='a')
self._register_mapper(registry, [])
command = self._makeOne(registry=registry)
result = command._find_view('/a', registry)
self.assertEqual(result, view)
def test__find_view_route_no_multiview(self):
from zope.interface import Interface
from zope.interface import implementer
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
from pyramid.registry import Registry
registry = Registry()
def view():pass
class IMyRoot(Interface):
pass
class IMyRoute(Interface):
pass
registry.registerAdapter(view,
(IViewClassifier, IMyRoute, IMyRoot),
IView, '')
registry.registerUtility(IMyRoute, IRouteRequest, name='a')
@implementer(IMyRoot)
class Factory(object):
def __init__(self, request):
pass
routes = [dummy.DummyRoute('a', '/a', factory=Factory, matchdict={}),
dummy.DummyRoute('b', '/b', factory=Factory)]
self._register_mapper(registry, routes)
command = self._makeOne(registry=registry)
result = command._find_view('/a', registry)
self.assertEqual(result, view)
def test__find_view_route_multiview_no_view_registered(self):
from zope.interface import Interface
from zope.interface import implementer
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IMultiView
from pyramid.interfaces import IRootFactory
from pyramid.registry import Registry
registry = Registry()
def view1():pass
def view2():pass
class IMyRoot(Interface):
pass
class IMyRoute1(Interface):
pass
class IMyRoute2(Interface):
pass
registry.registerUtility(IMyRoute1, IRouteRequest, name='a')
registry.registerUtility(IMyRoute2, IRouteRequest, name='b')
@implementer(IMyRoot)
class Factory(object):
def __init__(self, request):
pass
registry.registerUtility(Factory, IRootFactory)
routes = [dummy.DummyRoute('a', '/a', matchdict={}),
dummy.DummyRoute('b', '/a', matchdict={})]
self._register_mapper(registry, routes)
command = self._makeOne(registry=registry)
result = command._find_view('/a', registry)
self.assertTrue(IMultiView.providedBy(result))
def test__find_view_route_multiview(self):
from zope.interface import Interface
from zope.interface import implementer
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
from pyramid.interfaces import IMultiView
from pyramid.interfaces import IRootFactory
from pyramid.registry import Registry
registry = Registry()
def view1():pass
def view2():pass
class IMyRoot(Interface):
pass
class IMyRoute1(Interface):
pass
class IMyRoute2(Interface):
pass
registry.registerAdapter(view1,
(IViewClassifier, IMyRoute1, IMyRoot),
IView, '')
registry.registerAdapter(view2,
(IViewClassifier, IMyRoute2, IMyRoot),
IView, '')
registry.registerUtility(IMyRoute1, IRouteRequest, name='a')
registry.registerUtility(IMyRoute2, IRouteRequest, name='b')
@implementer(IMyRoot)
class Factory(object):
def __init__(self, request):
pass
registry.registerUtility(Factory, IRootFactory)
routes = [dummy.DummyRoute('a', '/a', matchdict={}),
dummy.DummyRoute('b', '/a', matchdict={})]
self._register_mapper(registry, routes)
command = self._makeOne(registry=registry)
result = command._find_view('/a', registry)
self.assertTrue(IMultiView.providedBy(result))
self.assertEqual(len(result.views), 2)
self.assertTrue((None, view1, None) in result.views)
self.assertTrue((None, view2, None) in result.views)
def test__find_multi_routes_all_match(self):
command = self._makeOne()
def factory(request): pass
routes = [dummy.DummyRoute('a', '/a', factory=factory, matchdict={}),
dummy.DummyRoute('b', '/a', factory=factory, matchdict={})]
mapper = dummy.DummyMapper(*routes)
request = dummy.DummyRequest({'PATH_INFO':'/a'})
result = command._find_multi_routes(mapper, request)
self.assertEqual(result, [{'match':{}, 'route':routes[0]},
{'match':{}, 'route':routes[1]}])
def test__find_multi_routes_some_match(self):
command = self._makeOne()
def factory(request): pass
routes = [dummy.DummyRoute('a', '/a', factory=factory),
dummy.DummyRoute('b', '/a', factory=factory, matchdict={})]
mapper = dummy.DummyMapper(*routes)
request = dummy.DummyRequest({'PATH_INFO':'/a'})
result = command._find_multi_routes(mapper, request)
self.assertEqual(result, [{'match':{}, 'route':routes[1]}])
def test__find_multi_routes_none_match(self):
command = self._makeOne()
def factory(request): pass
routes = [dummy.DummyRoute('a', '/a', factory=factory),
dummy.DummyRoute('b', '/a', factory=factory)]
mapper = dummy.DummyMapper(*routes)
request = dummy.DummyRequest({'PATH_INFO':'/a'})
result = command._find_multi_routes(mapper, request)
self.assertEqual(result, [])
def test_views_command_not_found(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
command._find_view = lambda arg1, arg2: None
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' Not found.')
def test_views_command_not_found_url_starts_without_slash(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
command._find_view = lambda arg1, arg2: None
command.args = ('/foo/bar/myapp.ini#myapp', 'a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' Not found.')
def test_views_command_single_view_traversal(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
view = dummy.DummyView(context='context', view_name='a')
command._find_view = lambda arg1, arg2: view
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.DummyView')
def test_views_command_single_view_function_traversal(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
def view(): pass
view.__request_attrs__ = {'context': 'context', 'view_name': 'a'}
command._find_view = lambda arg1, arg2: view
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.test_pviews.view')
def test_views_command_single_view_traversal_with_permission(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
view = dummy.DummyView(context='context', view_name='a')
view.__permission__ = 'test'
command._find_view = lambda arg1, arg2: view
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.DummyView')
self.assertEqual(L[9], ' required permission = test')
def test_views_command_single_view_traversal_with_predicates(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
def predicate(): pass
predicate.text = lambda *arg: "predicate = x"
view = dummy.DummyView(context='context', view_name='a')
view.__predicates__ = [predicate]
command._find_view = lambda arg1, arg2: view
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.DummyView')
self.assertEqual(L[9], ' view predicates (predicate = x)')
def test_views_command_single_view_route(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
route = dummy.DummyRoute('a', '/a', matchdict={})
view = dummy.DummyView(context='context', view_name='a',
matched_route=route, subpath='')
command._find_view = lambda arg1, arg2: view
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[6], ' Route:')
self.assertEqual(L[8], ' route name: a')
self.assertEqual(L[9], ' route pattern: /a')
self.assertEqual(L[10], ' route path: /a')
self.assertEqual(L[11], ' subpath: ')
self.assertEqual(L[15],
' pyramid.tests.test_scripts.dummy.DummyView')
def test_views_command_multi_view_nested(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
view1 = dummy.DummyView(context='context', view_name='a1')
view1.__name__ = 'view1'
view1.__view_attr__ = 'call'
multiview1 = dummy.DummyMultiView(view1, context='context',
view_name='a1')
multiview2 = dummy.DummyMultiView(multiview1, context='context',
view_name='a')
command._find_view = lambda arg1, arg2: multiview2
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.DummyMultiView')
self.assertEqual(L[12],
' pyramid.tests.test_scripts.dummy.view1.call')
def test_views_command_single_view_route_with_route_predicates(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
def predicate(): pass
predicate.text = lambda *arg: "predicate = x"
route = dummy.DummyRoute('a', '/a', matchdict={}, predicate=predicate)
view = dummy.DummyView(context='context', view_name='a',
matched_route=route, subpath='')
command._find_view = lambda arg1, arg2: view
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[6], ' Route:')
self.assertEqual(L[8], ' route name: a')
self.assertEqual(L[9], ' route pattern: /a')
self.assertEqual(L[10], ' route path: /a')
self.assertEqual(L[11], ' subpath: ')
self.assertEqual(L[12], ' route predicates (predicate = x)')
self.assertEqual(L[16],
' pyramid.tests.test_scripts.dummy.DummyView')
def test_views_command_multiview(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
view = dummy.DummyView(context='context')
view.__name__ = 'view'
view.__view_attr__ = 'call'
multiview = dummy.DummyMultiView(view, context='context', view_name='a')
command._find_view = lambda arg1, arg2: multiview
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.view.call')
def test_views_command_multiview_with_permission(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
view = dummy.DummyView(context='context')
view.__name__ = 'view'
view.__view_attr__ = 'call'
view.__permission__ = 'test'
multiview = dummy.DummyMultiView(view, context='context', view_name='a')
command._find_view = lambda arg1, arg2: multiview
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.view.call')
self.assertEqual(L[9], ' required permission = test')
def test_views_command_multiview_with_predicates(self):
from pyramid.registry import Registry
registry = Registry()
command = self._makeOne(registry=registry)
L = []
command.out = L.append
def predicate(): pass
predicate.text = lambda *arg: "predicate = x"
view = dummy.DummyView(context='context')
view.__name__ = 'view'
view.__view_attr__ = 'call'
view.__predicates__ = [predicate]
multiview = dummy.DummyMultiView(view, context='context', view_name='a')
command._find_view = lambda arg1, arg2: multiview
command.args = ('/foo/bar/myapp.ini#myapp', '/a')
result = command.run()
self.assertEqual(result, 0)
self.assertEqual(L[1], 'URL = /a')
self.assertEqual(L[3], ' context: context')
self.assertEqual(L[4], ' view name: a')
self.assertEqual(L[8],
' pyramid.tests.test_scripts.dummy.view.call')
self.assertEqual(L[9], ' view predicates (predicate = x)')
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.pviews import main
return main(argv, quiet=True)
def test_it(self):
result = self._callFUT(['pviews'])
self.assertEqual(result, 2)
pyramid-1.4.5/pyramid/tests/test_authorization.py 0000664 0001750 0001750 00000022356 12210154276 021612 0 ustar takaki takaki import unittest
from pyramid.testing import cleanUp
class TestACLAuthorizationPolicy(unittest.TestCase):
def setUp(self):
cleanUp()
def tearDown(self):
cleanUp()
def _getTargetClass(self):
from pyramid.authorization import ACLAuthorizationPolicy
return ACLAuthorizationPolicy
def _makeOne(self):
return self._getTargetClass()()
def test_class_implements_IAuthorizationPolicy(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import IAuthorizationPolicy
verifyClass(IAuthorizationPolicy, self._getTargetClass())
def test_instance_implements_IAuthorizationPolicy(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import IAuthorizationPolicy
verifyObject(IAuthorizationPolicy, self._makeOne())
def test_permits_no_acl(self):
context = DummyContext()
policy = self._makeOne()
self.assertEqual(policy.permits(context, [], 'view'), False)
def test_permits(self):
from pyramid.security import Deny
from pyramid.security import Allow
from pyramid.security import Everyone
from pyramid.security import Authenticated
from pyramid.security import ALL_PERMISSIONS
from pyramid.security import DENY_ALL
root = DummyContext()
community = DummyContext(__name__='community', __parent__=root)
blog = DummyContext(__name__='blog', __parent__=community)
root.__acl__ = [
(Allow, Authenticated, VIEW),
]
community.__acl__ = [
(Allow, 'fred', ALL_PERMISSIONS),
(Allow, 'wilma', VIEW),
DENY_ALL,
]
blog.__acl__ = [
(Allow, 'barney', MEMBER_PERMS),
(Allow, 'wilma', VIEW),
]
policy = self._makeOne()
result = policy.permits(blog, [Everyone, Authenticated, 'wilma'],
'view')
self.assertEqual(result, True)
self.assertEqual(result.context, blog)
self.assertEqual(result.ace, (Allow, 'wilma', VIEW))
self.assertEqual(result.acl, blog.__acl__)
result = policy.permits(blog, [Everyone, Authenticated, 'wilma'],
'delete')
self.assertEqual(result, False)
self.assertEqual(result.context, community)
self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS))
self.assertEqual(result.acl, community.__acl__)
result = policy.permits(blog, [Everyone, Authenticated, 'fred'], 'view')
self.assertEqual(result, True)
self.assertEqual(result.context, community)
self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS))
result = policy.permits(blog, [Everyone, Authenticated, 'fred'],
'doesntevenexistyet')
self.assertEqual(result, True)
self.assertEqual(result.context, community)
self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS))
self.assertEqual(result.acl, community.__acl__)
result = policy.permits(blog, [Everyone, Authenticated, 'barney'],
'view')
self.assertEqual(result, True)
self.assertEqual(result.context, blog)
self.assertEqual(result.ace, (Allow, 'barney', MEMBER_PERMS))
result = policy.permits(blog, [Everyone, Authenticated, 'barney'],
'administer')
self.assertEqual(result, False)
self.assertEqual(result.context, community)
self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS))
self.assertEqual(result.acl, community.__acl__)
result = policy.permits(root, [Everyone, Authenticated, 'someguy'],
'view')
self.assertEqual(result, True)
self.assertEqual(result.context, root)
self.assertEqual(result.ace, (Allow, Authenticated, VIEW))
result = policy.permits(blog,
[Everyone, Authenticated, 'someguy'], 'view')
self.assertEqual(result, False)
self.assertEqual(result.context, community)
self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS))
self.assertEqual(result.acl, community.__acl__)
result = policy.permits(root, [Everyone], 'view')
self.assertEqual(result, False)
self.assertEqual(result.context, root)
self.assertEqual(result.ace, '')
self.assertEqual(result.acl, root.__acl__)
context = DummyContext()
result = policy.permits(context, [Everyone], 'view')
self.assertEqual(result, False)
self.assertEqual(result.ace, '')
self.assertEqual(
result.acl,
'')
def test_permits_string_permissions_in_acl(self):
from pyramid.security import Allow
root = DummyContext()
root.__acl__ = [
(Allow, 'wilma', 'view_stuff'),
]
policy = self._makeOne()
result = policy.permits(root, ['wilma'], 'view')
# would be True if matching against 'view_stuff' instead of against
# ['view_stuff']
self.assertEqual(result, False)
def test_principals_allowed_by_permission_direct(self):
from pyramid.security import Allow
from pyramid.security import DENY_ALL
context = DummyContext()
acl = [ (Allow, 'chrism', ('read', 'write')),
DENY_ALL,
(Allow, 'other', 'read') ]
context.__acl__ = acl
policy = self._makeOne()
result = sorted(
policy.principals_allowed_by_permission(context, 'read'))
self.assertEqual(result, ['chrism'])
def test_principals_allowed_by_permission_string_permission(self):
from pyramid.security import Allow
context = DummyContext()
acl = [ (Allow, 'chrism', 'read_it')]
context.__acl__ = acl
policy = self._makeOne()
result = policy.principals_allowed_by_permission(context, 'read')
# would be ['chrism'] if 'read' were compared against 'read_it' instead
# of against ['read_it']
self.assertEqual(list(result), [])
def test_principals_allowed_by_permission(self):
from pyramid.security import Allow
from pyramid.security import Deny
from pyramid.security import DENY_ALL
from pyramid.security import ALL_PERMISSIONS
root = DummyContext(__name__='', __parent__=None)
community = DummyContext(__name__='community', __parent__=root)
blog = DummyContext(__name__='blog', __parent__=community)
root.__acl__ = [ (Allow, 'chrism', ('read', 'write')),
(Allow, 'other', ('read',)),
(Allow, 'jim', ALL_PERMISSIONS)]
community.__acl__ = [ (Deny, 'flooz', 'read'),
(Allow, 'flooz', 'read'),
(Allow, 'mork', 'read'),
(Deny, 'jim', 'read'),
(Allow, 'someguy', 'manage')]
blog.__acl__ = [ (Allow, 'fred', 'read'),
DENY_ALL]
policy = self._makeOne()
result = sorted(policy.principals_allowed_by_permission(blog, 'read'))
self.assertEqual(result, ['fred'])
result = sorted(policy.principals_allowed_by_permission(community,
'read'))
self.assertEqual(result, ['chrism', 'mork', 'other'])
result = sorted(policy.principals_allowed_by_permission(community,
'read'))
result = sorted(policy.principals_allowed_by_permission(root, 'read'))
self.assertEqual(result, ['chrism', 'jim', 'other'])
def test_principals_allowed_by_permission_no_acls(self):
context = DummyContext()
policy = self._makeOne()
result = sorted(policy.principals_allowed_by_permission(context,'read'))
self.assertEqual(result, [])
def test_principals_allowed_by_permission_deny_not_permission_in_acl(self):
from pyramid.security import Deny
from pyramid.security import Everyone
context = DummyContext()
acl = [ (Deny, Everyone, 'write') ]
context.__acl__ = acl
policy = self._makeOne()
result = sorted(
policy.principals_allowed_by_permission(context, 'read'))
self.assertEqual(result, [])
def test_principals_allowed_by_permission_deny_permission_in_acl(self):
from pyramid.security import Deny
from pyramid.security import Everyone
context = DummyContext()
acl = [ (Deny, Everyone, 'read') ]
context.__acl__ = acl
policy = self._makeOne()
result = sorted(
policy.principals_allowed_by_permission(context, 'read'))
self.assertEqual(result, [])
class DummyContext:
def __init__(self, *arg, **kw):
self.__dict__.update(kw)
VIEW = 'view'
EDIT = 'edit'
CREATE = 'create'
DELETE = 'delete'
MODERATE = 'moderate'
ADMINISTER = 'administer'
COMMENT = 'comment'
GUEST_PERMS = (VIEW, COMMENT)
MEMBER_PERMS = GUEST_PERMS + (EDIT, CREATE, DELETE)
MODERATOR_PERMS = MEMBER_PERMS + (MODERATE,)
ADMINISTRATOR_PERMS = MODERATOR_PERMS + (ADMINISTER,)
pyramid-1.4.5/pyramid/tests/test_view.py 0000664 0001750 0001750 00000072135 12203712502 017656 0 ustar takaki takaki import unittest
import sys
from zope.interface import implementer
from pyramid import testing
class BaseTest(object):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
def _registerView(self, reg, app, name):
from pyramid.interfaces import IRequest
from pyramid.interfaces import IViewClassifier
for_ = (IViewClassifier, IRequest, IContext)
from pyramid.interfaces import IView
reg.registerAdapter(app, for_, IView, name)
def _makeEnviron(self, **extras):
environ = {
'wsgi.url_scheme':'http',
'wsgi.version':(1,0),
'SERVER_NAME':'localhost',
'SERVER_PORT':'8080',
'REQUEST_METHOD':'GET',
'PATH_INFO':'/',
}
environ.update(extras)
return environ
def _makeRequest(self, **environ):
from pyramid.interfaces import IRequest
from zope.interface import directlyProvides
from webob import Request
from pyramid.registry import Registry
environ = self._makeEnviron(**environ)
request = Request(environ)
request.registry = Registry()
directlyProvides(request, IRequest)
return request
def _makeContext(self):
from zope.interface import directlyProvides
context = DummyContext()
directlyProvides(context, IContext)
return context
class Test_notfound_view_config(BaseTest, unittest.TestCase):
def _makeOne(self, **kw):
from pyramid.view import notfound_view_config
return notfound_view_config(**kw)
def test_ctor(self):
inst = self._makeOne(attr='attr', path_info='path_info',
append_slash=True)
self.assertEqual(inst.__dict__,
{'attr':'attr', 'path_info':'path_info',
'append_slash':True})
def test_it_function(self):
def view(request): pass
decorator = self._makeOne(attr='attr', renderer='renderer',
append_slash=True)
venusian = DummyVenusian()
decorator.venusian = venusian
wrapped = decorator(view)
self.assertTrue(wrapped is view)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(
settings,
[{'attr': 'attr', 'venusian': venusian, 'append_slash': True,
'renderer': 'renderer', '_info': 'codeinfo', 'view': None}]
)
def test_it_class(self):
decorator = self._makeOne()
venusian = DummyVenusian()
decorator.venusian = venusian
decorator.venusian.info.scope = 'class'
class view(object): pass
wrapped = decorator(view)
self.assertTrue(wrapped is view)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
self.assertEqual(len(settings[0]), 4)
self.assertEqual(settings[0]['venusian'], venusian)
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['attr'], 'view')
self.assertEqual(settings[0]['_info'], 'codeinfo')
class Test_forbidden_view_config(BaseTest, unittest.TestCase):
def _makeOne(self, **kw):
from pyramid.view import forbidden_view_config
return forbidden_view_config(**kw)
def test_ctor(self):
inst = self._makeOne(attr='attr', path_info='path_info')
self.assertEqual(inst.__dict__,
{'attr':'attr', 'path_info':'path_info'})
def test_it_function(self):
def view(request): pass
decorator = self._makeOne(attr='attr', renderer='renderer')
venusian = DummyVenusian()
decorator.venusian = venusian
wrapped = decorator(view)
self.assertTrue(wrapped is view)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(
settings,
[{'attr': 'attr', 'venusian': venusian,
'renderer': 'renderer', '_info': 'codeinfo', 'view': None}]
)
def test_it_class(self):
decorator = self._makeOne()
venusian = DummyVenusian()
decorator.venusian = venusian
decorator.venusian.info.scope = 'class'
class view(object): pass
wrapped = decorator(view)
self.assertTrue(wrapped is view)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
self.assertEqual(len(settings[0]), 4)
self.assertEqual(settings[0]['venusian'], venusian)
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['attr'], 'view')
self.assertEqual(settings[0]['_info'], 'codeinfo')
class RenderViewToResponseTests(BaseTest, unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.view import render_view_to_response
return render_view_to_response(*arg, **kw)
def test_call_no_view_registered(self):
request = self._makeRequest()
context = self._makeContext()
result = self._callFUT(context, request, name='notregistered')
self.assertEqual(result, None)
def test_call_no_registry_on_request(self):
request = self._makeRequest()
del request.registry
context = self._makeContext()
result = self._callFUT(context, request, name='notregistered')
self.assertEqual(result, None)
def test_call_view_registered_secure(self):
request = self._makeRequest()
context = self._makeContext()
response = DummyResponse()
view = make_view(response)
self._registerView(request.registry, view, 'registered')
response = self._callFUT(context, request, name='registered',
secure=True)
self.assertEqual(response.status, '200 OK')
def test_call_view_registered_insecure_no_call_permissive(self):
context = self._makeContext()
request = self._makeRequest()
response = DummyResponse()
view = make_view(response)
self._registerView(request.registry, view, 'registered')
response = self._callFUT(context, request, name='registered',
secure=False)
self.assertEqual(response.status, '200 OK')
def test_call_view_registered_insecure_with_call_permissive(self):
context = self._makeContext()
request = self._makeRequest()
response = DummyResponse()
view = make_view(response)
def anotherview(context, request):
return DummyResponse('anotherview')
view.__call_permissive__ = anotherview
self._registerView(request.registry, view, 'registered')
response = self._callFUT(context, request, name='registered',
secure=False)
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.app_iter, ['anotherview'])
class RenderViewToIterableTests(BaseTest, unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.view import render_view_to_iterable
return render_view_to_iterable(*arg, **kw)
def test_call_no_view_registered(self):
request = self._makeRequest()
context = self._makeContext()
result = self._callFUT(context, request, name='notregistered')
self.assertEqual(result, None)
def test_call_view_registered_secure(self):
request = self._makeRequest()
context = self._makeContext()
response = DummyResponse()
view = make_view(response)
self._registerView(request.registry, view, 'registered')
iterable = self._callFUT(context, request, name='registered',
secure=True)
self.assertEqual(iterable, ())
def test_call_view_registered_insecure_no_call_permissive(self):
context = self._makeContext()
request = self._makeRequest()
response = DummyResponse()
view = make_view(response)
self._registerView(request.registry, view, 'registered')
iterable = self._callFUT(context, request, name='registered',
secure=False)
self.assertEqual(iterable, ())
def test_call_view_registered_insecure_with_call_permissive(self):
context = self._makeContext()
request = self._makeRequest()
response = DummyResponse()
view = make_view(response)
def anotherview(context, request):
return DummyResponse(b'anotherview')
view.__call_permissive__ = anotherview
self._registerView(request.registry, view, 'registered')
iterable = self._callFUT(context, request, name='registered',
secure=False)
self.assertEqual(iterable, [b'anotherview'])
def test_verify_output_bytestring(self):
from pyramid.request import Request
from pyramid.config import Configurator
from pyramid.view import render_view
from webob.compat import text_type
config = Configurator(settings={})
def view(request):
request.response.text = text_type('')
return request.response
config.add_view(name='test', view=view)
config.commit()
r = Request({})
r.registry = config.registry
self.assertEqual(render_view(object(), r, 'test'), b'')
def test_call_request_has_no_registry(self):
request = self._makeRequest()
del request.registry
registry = self.config.registry
context = self._makeContext()
response = DummyResponse()
view = make_view(response)
self._registerView(registry, view, 'registered')
iterable = self._callFUT(context, request, name='registered',
secure=True)
self.assertEqual(iterable, ())
class RenderViewTests(BaseTest, unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.view import render_view
return render_view(*arg, **kw)
def test_call_no_view_registered(self):
request = self._makeRequest()
context = self._makeContext()
result = self._callFUT(context, request, name='notregistered')
self.assertEqual(result, None)
def test_call_view_registered_secure(self):
request = self._makeRequest()
context = self._makeContext()
response = DummyResponse()
view = make_view(response)
self._registerView(request.registry, view, 'registered')
s = self._callFUT(context, request, name='registered', secure=True)
self.assertEqual(s, b'')
def test_call_view_registered_insecure_no_call_permissive(self):
context = self._makeContext()
request = self._makeRequest()
response = DummyResponse()
view = make_view(response)
self._registerView(request.registry, view, 'registered')
s = self._callFUT(context, request, name='registered', secure=False)
self.assertEqual(s, b'')
def test_call_view_registered_insecure_with_call_permissive(self):
context = self._makeContext()
request = self._makeRequest()
response = DummyResponse()
view = make_view(response)
def anotherview(context, request):
return DummyResponse(b'anotherview')
view.__call_permissive__ = anotherview
self._registerView(request.registry, view, 'registered')
s = self._callFUT(context, request, name='registered', secure=False)
self.assertEqual(s, b'anotherview')
class TestIsResponse(unittest.TestCase):
def setUp(self):
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
from zope.deprecation import __show__
__show__.on()
def _callFUT(self, *arg, **kw):
from pyramid.view import is_response
return is_response(*arg, **kw)
def test_is(self):
response = DummyResponse()
self.assertEqual(self._callFUT(response), True)
def test_isnt(self):
response = None
self.assertEqual(self._callFUT(response), False)
def test_isnt_no_headerlist(self):
class Response(object):
pass
resp = Response
resp.status = '200 OK'
resp.app_iter = []
self.assertEqual(self._callFUT(resp), False)
def test_isnt_no_status(self):
class Response(object):
pass
resp = Response
resp.app_iter = []
resp.headerlist = ()
self.assertEqual(self._callFUT(resp), False)
def test_isnt_no_app_iter(self):
class Response(object):
pass
resp = Response
resp.status = '200 OK'
resp.headerlist = ()
self.assertEqual(self._callFUT(resp), False)
class TestViewConfigDecorator(unittest.TestCase):
def setUp(self):
testing.setUp()
def tearDown(self):
testing.tearDown()
def _getTargetClass(self):
from pyramid.view import view_config
return view_config
def _makeOne(self, *arg, **kw):
return self._getTargetClass()(*arg, **kw)
def test_create_defaults(self):
decorator = self._makeOne()
self.assertEqual(decorator.__dict__, {})
def test_create_context_trumps_for(self):
decorator = self._makeOne(context='123', for_='456')
self.assertEqual(decorator.context, '123')
def test_create_for_trumps_context_None(self):
decorator = self._makeOne(context=None, for_='456')
self.assertEqual(decorator.context, '456')
def test_create_nondefaults(self):
decorator = self._makeOne(
name=None, request_type=None, for_=None,
permission='foo', mapper='mapper',
decorator='decorator', match_param='match_param'
)
self.assertEqual(decorator.name, None)
self.assertEqual(decorator.request_type, None)
self.assertEqual(decorator.context, None)
self.assertEqual(decorator.permission, 'foo')
self.assertEqual(decorator.mapper, 'mapper')
self.assertEqual(decorator.decorator, 'decorator')
self.assertEqual(decorator.match_param, 'match_param')
def test_create_with_other_predicates(self):
decorator = self._makeOne(foo=1)
self.assertEqual(decorator.foo, 1)
def test_create_decorator_tuple(self):
decorator = self._makeOne(decorator=('decorator1', 'decorator2'))
self.assertEqual(decorator.decorator, ('decorator1', 'decorator2'))
def test_call_function(self):
decorator = self._makeOne()
venusian = DummyVenusian()
decorator.venusian = venusian
def foo(): pass
wrapped = decorator(foo)
self.assertTrue(wrapped is foo)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
self.assertEqual(len(settings), 1)
self.assertEqual(len(settings[0]), 3)
self.assertEqual(settings[0]['venusian'], venusian)
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['_info'], 'codeinfo')
def test_call_class(self):
decorator = self._makeOne()
venusian = DummyVenusian()
decorator.venusian = venusian
decorator.venusian.info.scope = 'class'
class foo(object): pass
wrapped = decorator(foo)
self.assertTrue(wrapped is foo)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
self.assertEqual(len(settings[0]), 4)
self.assertEqual(settings[0]['venusian'], venusian)
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['attr'], 'foo')
self.assertEqual(settings[0]['_info'], 'codeinfo')
def test_call_class_attr_already_set(self):
decorator = self._makeOne(attr='abc')
venusian = DummyVenusian()
decorator.venusian = venusian
decorator.venusian.info.scope = 'class'
class foo(object): pass
wrapped = decorator(foo)
self.assertTrue(wrapped is foo)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
self.assertEqual(len(settings[0]), 4)
self.assertEqual(settings[0]['venusian'], venusian)
self.assertEqual(settings[0]['view'], None) # comes from call_venusian
self.assertEqual(settings[0]['attr'], 'abc')
self.assertEqual(settings[0]['_info'], 'codeinfo')
def test_stacking(self):
decorator1 = self._makeOne(name='1')
venusian1 = DummyVenusian()
decorator1.venusian = venusian1
venusian2 = DummyVenusian()
decorator2 = self._makeOne(name='2')
decorator2.venusian = venusian2
def foo(): pass
wrapped1 = decorator1(foo)
wrapped2 = decorator2(wrapped1)
self.assertTrue(wrapped1 is foo)
self.assertTrue(wrapped2 is foo)
config1 = call_venusian(venusian1)
self.assertEqual(len(config1.settings), 1)
self.assertEqual(config1.settings[0]['name'], '1')
config2 = call_venusian(venusian2)
self.assertEqual(len(config2.settings), 1)
self.assertEqual(config2.settings[0]['name'], '2')
def test_call_as_method(self):
decorator = self._makeOne()
venusian = DummyVenusian()
decorator.venusian = venusian
decorator.venusian.info.scope = 'class'
def foo(self): pass
def bar(self): pass
class foo(object):
foomethod = decorator(foo)
barmethod = decorator(bar)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 2)
self.assertEqual(settings[0]['attr'], 'foo')
self.assertEqual(settings[1]['attr'], 'bar')
def test_with_custom_predicates(self):
decorator = self._makeOne(custom_predicates=(1,))
venusian = DummyVenusian()
decorator.venusian = venusian
def foo(context, request): pass
decorated = decorator(foo)
self.assertTrue(decorated is foo)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(settings[0]['custom_predicates'], (1,))
def test_call_with_renderer_string(self):
import pyramid.tests
decorator = self._makeOne(renderer='fixtures/minimal.pt')
venusian = DummyVenusian()
decorator.venusian = venusian
def foo(): pass
wrapped = decorator(foo)
self.assertTrue(wrapped is foo)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
renderer = settings[0]['renderer']
self.assertEqual(renderer, 'fixtures/minimal.pt')
self.assertEqual(config.pkg, pyramid.tests)
def test_call_with_renderer_dict(self):
import pyramid.tests
decorator = self._makeOne(renderer={'a':1})
venusian = DummyVenusian()
decorator.venusian = venusian
def foo(): pass
wrapped = decorator(foo)
self.assertTrue(wrapped is foo)
config = call_venusian(venusian)
settings = config.settings
self.assertEqual(len(settings), 1)
self.assertEqual(settings[0]['renderer'], {'a':1})
self.assertEqual(config.pkg, pyramid.tests)
def test_call_with_renderer_IRendererInfo(self):
import pyramid.tests
from pyramid.interfaces import IRendererInfo
@implementer(IRendererInfo)
class DummyRendererHelper(object):
pass
renderer_helper = DummyRendererHelper()
decorator = self._makeOne(renderer=renderer_helper)
venusian = DummyVenusian()
decorator.venusian = venusian
def foo(): pass
wrapped = decorator(foo)
self.assertTrue(wrapped is foo)
context = DummyVenusianContext()
config = call_venusian(venusian, context)
settings = config.settings
self.assertEqual(len(settings), 1)
renderer = settings[0]['renderer']
self.assertTrue(renderer is renderer_helper)
self.assertEqual(config.pkg, pyramid.tests)
def test_call_withdepth(self):
decorator = self._makeOne(_depth=1)
venusian = DummyVenusian()
decorator.venusian = venusian
def foo(): pass
decorator(foo)
self.assertEqual(venusian.depth, 2)
class Test_append_slash_notfound_view(BaseTest, unittest.TestCase):
def _callFUT(self, context, request):
from pyramid.view import append_slash_notfound_view
return append_slash_notfound_view(context, request)
def _registerMapper(self, reg, match=True):
from pyramid.interfaces import IRoutesMapper
class DummyRoute(object):
def __init__(self, val):
self.val = val
def match(self, path):
return self.val
class DummyMapper(object):
def __init__(self):
self.routelist = [ DummyRoute(match) ]
def get_routes(self):
return self.routelist
mapper = DummyMapper()
reg.registerUtility(mapper, IRoutesMapper)
return mapper
def test_context_is_not_exception(self):
request = self._makeRequest(PATH_INFO='/abc')
request.exception = ExceptionResponse()
context = DummyContext()
response = self._callFUT(context, request)
self.assertEqual(response.status, '404 Not Found')
self.assertEqual(response.app_iter, ['Not Found'])
def test_no_mapper(self):
request = self._makeRequest(PATH_INFO='/abc')
context = ExceptionResponse()
response = self._callFUT(context, request)
self.assertEqual(response.status, '404 Not Found')
def test_no_path(self):
request = self._makeRequest()
context = ExceptionResponse()
self._registerMapper(request.registry, True)
response = self._callFUT(context, request)
self.assertEqual(response.status, '404 Not Found')
def test_mapper_path_already_slash_ending(self):
request = self._makeRequest(PATH_INFO='/abc/')
context = ExceptionResponse()
self._registerMapper(request.registry, True)
response = self._callFUT(context, request)
self.assertEqual(response.status, '404 Not Found')
def test_no_route_matches(self):
request = self._makeRequest(PATH_INFO='/abc')
context = ExceptionResponse()
mapper = self._registerMapper(request.registry, True)
mapper.routelist[0].val = None
response = self._callFUT(context, request)
self.assertEqual(response.status, '404 Not Found')
def test_matches(self):
request = self._makeRequest(PATH_INFO='/abc')
context = ExceptionResponse()
self._registerMapper(request.registry, True)
response = self._callFUT(context, request)
self.assertEqual(response.status, '302 Found')
self.assertEqual(response.location, '/abc/')
def test_matches_with_script_name(self):
request = self._makeRequest(PATH_INFO='/abc', SCRIPT_NAME='/foo')
context = ExceptionResponse()
self._registerMapper(request.registry, True)
response = self._callFUT(context, request)
self.assertEqual(response.status, '302 Found')
self.assertEqual(response.location, '/foo/abc/')
def test_with_query_string(self):
request = self._makeRequest(PATH_INFO='/abc', QUERY_STRING='a=1&b=2')
context = ExceptionResponse()
self._registerMapper(request.registry, True)
response = self._callFUT(context, request)
self.assertEqual(response.status, '302 Found')
self.assertEqual(response.location, '/abc/?a=1&b=2')
class TestAppendSlashNotFoundViewFactory(BaseTest, unittest.TestCase):
def _makeOne(self, notfound_view):
from pyramid.view import AppendSlashNotFoundViewFactory
return AppendSlashNotFoundViewFactory(notfound_view)
def test_custom_notfound_view(self):
request = self._makeRequest(PATH_INFO='/abc')
context = ExceptionResponse()
def custom_notfound(context, request):
return 'OK'
view = self._makeOne(custom_notfound)
response = view(context, request)
self.assertEqual(response, 'OK')
class Test_default_exceptionresponse_view(unittest.TestCase):
def _callFUT(self, context, request):
from pyramid.view import default_exceptionresponse_view
return default_exceptionresponse_view(context, request)
def test_is_exception(self):
context = Exception()
result = self._callFUT(context, None)
self.assertTrue(result is context)
def test_is_not_exception_context_is_false_still_chose(self):
request = DummyRequest()
request.exception = 0
result = self._callFUT(None, request)
self.assertTrue(result is None)
def test_is_not_exception_no_request_exception(self):
context = object()
request = DummyRequest()
request.exception = None
result = self._callFUT(context, request)
self.assertTrue(result is context)
def test_is_not_exception_request_exception(self):
context = object()
request = DummyRequest()
request.exception = 'abc'
result = self._callFUT(context, request)
self.assertEqual(result, 'abc')
class Test_static(unittest.TestCase):
def setUp(self):
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
from zope.deprecation import __show__
__show__.on()
def _makeOne(self, path, package_name):
from pyramid.view import static
return static(path, package_name)
def test_it(self):
path = 'fixtures'
view = self._makeOne(path, None)
self.assertEqual(view.docroot, 'fixtures')
class Test_view_defaults(unittest.TestCase):
def test_it(self):
from pyramid.view import view_defaults
@view_defaults(route_name='abc', renderer='def')
class Foo(object): pass
self.assertEqual(Foo.__view_defaults__['route_name'],'abc')
self.assertEqual(Foo.__view_defaults__['renderer'],'def')
def test_it_inheritance_not_overridden(self):
from pyramid.view import view_defaults
@view_defaults(route_name='abc', renderer='def')
class Foo(object): pass
class Bar(Foo): pass
self.assertEqual(Bar.__view_defaults__['route_name'],'abc')
self.assertEqual(Bar.__view_defaults__['renderer'],'def')
def test_it_inheritance_overriden(self):
from pyramid.view import view_defaults
@view_defaults(route_name='abc', renderer='def')
class Foo(object): pass
@view_defaults(route_name='ghi')
class Bar(Foo): pass
self.assertEqual(Bar.__view_defaults__['route_name'],'ghi')
self.assertFalse('renderer' in Bar.__view_defaults__)
def test_it_inheritance_overriden_empty(self):
from pyramid.view import view_defaults
@view_defaults(route_name='abc', renderer='def')
class Foo(object): pass
@view_defaults()
class Bar(Foo): pass
self.assertEqual(Bar.__view_defaults__, {})
class ExceptionResponse(Exception):
status = '404 Not Found'
app_iter = ['Not Found']
headerlist = []
class DummyContext:
pass
def make_view(response):
def view(context, request):
return response
return view
class DummyRequest:
exception = None
def __init__(self, environ=None):
if environ is None:
environ = {}
self.environ = environ
from pyramid.interfaces import IResponse
@implementer(IResponse)
class DummyResponse(object):
headerlist = ()
app_iter = ()
status = '200 OK'
environ = None
def __init__(self, body=None):
if body is None:
self.app_iter = ()
else:
self.app_iter = [body]
from zope.interface import Interface
class IContext(Interface):
pass
class DummyVenusianInfo(object):
scope = 'notaclass'
module = sys.modules['pyramid.tests']
codeinfo = 'codeinfo'
class DummyVenusian(object):
def __init__(self, info=None):
if info is None:
info = DummyVenusianInfo()
self.info = info
self.attachments = []
def attach(self, wrapped, callback, category=None, depth=1):
self.attachments.append((wrapped, callback, category))
self.depth = depth
return self.info
class DummyRegistry(object):
pass
class DummyConfig(object):
def __init__(self):
self.settings = []
self.registry = DummyRegistry()
def add_view(self, **kw):
self.settings.append(kw)
add_notfound_view = add_forbidden_view = add_view
def with_package(self, pkg):
self.pkg = pkg
return self
class DummyVenusianContext(object):
def __init__(self):
self.config = DummyConfig()
def call_venusian(venusian, context=None):
if context is None:
context = DummyVenusianContext()
for wrapped, callback, category in venusian.attachments:
callback(context, None, None)
return context.config
pyramid-1.4.5/pyramid/tests/test_asset.py 0000664 0001750 0001750 00000006133 12203712502 020016 0 ustar takaki takaki import unittest
import os
here = os.path.abspath(os.path.dirname(__file__))
class Test_resolve_asset_spec(unittest.TestCase):
def _callFUT(self, spec, package_name='__main__'):
from pyramid.resource import resolve_asset_spec
return resolve_asset_spec(spec, package_name)
def test_abspath(self):
package_name, filename = self._callFUT(here, 'apackage')
self.assertEqual(filename, here)
self.assertEqual(package_name, None)
def test_rel_spec(self):
pkg = 'pyramid.tests'
path = 'test_asset.py'
package_name, filename = self._callFUT(path, pkg)
self.assertEqual(package_name, 'pyramid.tests')
self.assertEqual(filename, 'test_asset.py')
def test_abs_spec(self):
pkg = 'pyramid.tests'
path = 'pyramid.nottests:test_asset.py'
package_name, filename = self._callFUT(path, pkg)
self.assertEqual(package_name, 'pyramid.nottests')
self.assertEqual(filename, 'test_asset.py')
def test_package_name_is_None(self):
pkg = None
path = 'test_asset.py'
package_name, filename = self._callFUT(path, pkg)
self.assertEqual(package_name, None)
self.assertEqual(filename, 'test_asset.py')
def test_package_name_is_package_object(self):
import pyramid.tests
pkg = pyramid.tests
path = 'test_asset.py'
package_name, filename = self._callFUT(path, pkg)
self.assertEqual(package_name, 'pyramid.tests')
self.assertEqual(filename, 'test_asset.py')
class Test_abspath_from_asset_spec(unittest.TestCase):
def _callFUT(self, spec, pname='__main__'):
from pyramid.resource import abspath_from_asset_spec
return abspath_from_asset_spec(spec, pname)
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_asset_spec(self):
result = self._callFUT('/abc', '__main__')
self.assertEqual(result, '/abc')
def test_pkgrelative(self):
result = self._callFUT('abc', 'pyramid.tests')
self.assertEqual(result, os.path.join(here, 'abc'))
class Test_asset_spec_from_abspath(unittest.TestCase):
def _callFUT(self, abspath, package):
from pyramid.asset import asset_spec_from_abspath
return asset_spec_from_abspath(abspath, package)
def test_package_name_is_main(self):
pkg = DummyPackage('__main__')
result = self._callFUT('abspath', pkg)
self.assertEqual(result, 'abspath')
def test_abspath_startswith_package_path(self):
abspath = os.path.join(here, 'fixtureapp')
pkg = DummyPackage('pyramid.tests')
pkg.__file__ = 'file'
result = self._callFUT(abspath, pkg)
self.assertEqual(result, 'pyramid:fixtureapp')
def test_abspath_doesnt_startwith_package_path(self):
pkg = DummyPackage('pyramid.tests')
result = self._callFUT(here, pkg)
self.assertEqual(result, here)
class DummyPackage:
def __init__(self, name):
self.__name__ = name
pyramid-1.4.5/pyramid/tests/test_encode.py 0000664 0001750 0001750 00000003737 12203712502 020143 0 ustar takaki takaki import unittest
from pyramid.compat import (
text_,
native_,
)
class UrlEncodeTests(unittest.TestCase):
def _callFUT(self, query, doseq=False):
from pyramid.encode import urlencode
return urlencode(query, doseq)
def test_ascii_only(self):
result = self._callFUT([('a',1), ('b',2)])
self.assertEqual(result, 'a=1&b=2')
def test_unicode_key(self):
la = text_(b'LaPe\xc3\xb1a', 'utf-8')
result = self._callFUT([(la, 1), ('b',2)])
self.assertEqual(result, 'LaPe%C3%B1a=1&b=2')
def test_unicode_val_single(self):
la = text_(b'LaPe\xc3\xb1a', 'utf-8')
result = self._callFUT([('a', la), ('b',2)])
self.assertEqual(result, 'a=LaPe%C3%B1a&b=2')
def test_unicode_val_multiple(self):
la = [text_(b'LaPe\xc3\xb1a', 'utf-8')] * 2
result = self._callFUT([('a', la), ('b',2)], doseq=True)
self.assertEqual(result, 'a=LaPe%C3%B1a&a=LaPe%C3%B1a&b=2')
def test_int_val_multiple(self):
s = [1, 2]
result = self._callFUT([('a', s)], doseq=True)
self.assertEqual(result, 'a=1&a=2')
def test_with_spaces(self):
result = self._callFUT([('a', '123 456')], doseq=True)
self.assertEqual(result, 'a=123+456')
def test_dict(self):
result = self._callFUT({'a':1})
self.assertEqual(result, 'a=1')
class URLQuoteTests(unittest.TestCase):
def _callFUT(self, val, safe=''):
from pyramid.encode import url_quote
return url_quote(val, safe)
def test_it_bytes(self):
la = b'La/Pe\xc3\xb1a'
result = self._callFUT(la)
self.assertEqual(result, 'La%2FPe%C3%B1a')
def test_it_native(self):
la = native_(b'La/Pe\xc3\xb1a', 'utf-8')
result = self._callFUT(la)
self.assertEqual(result, 'La%2FPe%C3%B1a')
def test_it_with_safe(self):
la = b'La/Pe\xc3\xb1a'
result = self._callFUT(la, '/')
self.assertEqual(result, 'La/Pe%C3%B1a')
pyramid-1.4.5/pyramid/tests/test_session.py 0000664 0001750 0001750 00000035650 12210154276 020376 0 ustar takaki takaki import unittest
from pyramid import testing
class TestUnencryptedCookieSession(unittest.TestCase):
def _makeOne(self, request, **kw):
from pyramid.session import UnencryptedCookieSessionFactoryConfig
return UnencryptedCookieSessionFactoryConfig('secret', **kw)(request)
def test_ctor_no_cookie(self):
request = testing.DummyRequest()
session = self._makeOne(request)
self.assertEqual(dict(session), {})
def test_instance_conforms(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import ISession
request = testing.DummyRequest()
session = self._makeOne(request)
verifyObject(ISession, session)
def _serialize(self, accessed, state, secret='secret'):
from pyramid.session import signed_serialize
return signed_serialize((accessed, accessed, state), secret)
def test_ctor_with_cookie_still_valid(self):
import time
request = testing.DummyRequest()
cookieval = self._serialize(time.time(), {'state':1})
request.cookies['session'] = cookieval
session = self._makeOne(request)
self.assertEqual(dict(session), {'state':1})
def test_ctor_with_cookie_expired(self):
request = testing.DummyRequest()
cookieval = self._serialize(0, {'state':1})
request.cookies['session'] = cookieval
session = self._makeOne(request)
self.assertEqual(dict(session), {})
def test_ctor_with_bad_cookie(self):
request = testing.DummyRequest()
cookieval = 'abc'
request.cookies['session'] = cookieval
session = self._makeOne(request)
self.assertEqual(dict(session), {})
def test_changed(self):
request = testing.DummyRequest()
session = self._makeOne(request)
self.assertEqual(session.changed(), None)
def test_invalidate(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session['a'] = 1
self.assertEqual(session.invalidate(), None)
self.assertFalse('a' in session)
def test__set_cookie_on_exception(self):
request = testing.DummyRequest()
request.exception = True
session = self._makeOne(request)
session._cookie_on_exception = False
response = DummyResponse()
self.assertEqual(session._set_cookie(response), False)
def test__set_cookie_on_exception_no_request_exception(self):
import webob
request = testing.DummyRequest()
request.exception = None
session = self._makeOne(request)
session._cookie_on_exception = False
response = webob.Response()
self.assertEqual(session._set_cookie(response), True)
self.assertEqual(response.headerlist[-1][0], 'Set-Cookie')
def test__set_cookie_cookieval_too_long(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session['abc'] = 'x'*100000
response = DummyResponse()
self.assertRaises(ValueError, session._set_cookie, response)
def test__set_cookie_real_webob_response(self):
import webob
request = testing.DummyRequest()
session = self._makeOne(request)
session['abc'] = 'x'
response = webob.Response()
self.assertEqual(session._set_cookie(response), True)
self.assertEqual(response.headerlist[-1][0], 'Set-Cookie')
def test__set_cookie_options(self):
from pyramid.response import Response
request = testing.DummyRequest()
request.exception = None
session = self._makeOne(request,
cookie_name = 'abc',
cookie_path = '/foo',
cookie_domain = 'localhost',
cookie_secure = True,
cookie_httponly = True,
)
session['abc'] = 'x'
response = Response()
self.assertEqual(session._set_cookie(response), True)
cookieval= response.headerlist[-1][1]
val, domain, path, secure, httponly = [x.strip() for x in
cookieval.split(';')]
self.assertTrue(val.startswith('abc='))
self.assertEqual(domain, 'Domain=localhost')
self.assertEqual(path, 'Path=/foo')
self.assertEqual(secure, 'secure')
self.assertEqual(httponly, 'HttpOnly')
def test_flash_default(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session.flash('msg1')
session.flash('msg2')
self.assertEqual(session['_f_'], ['msg1', 'msg2'])
def test_flash_allow_duplicate_false(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session.flash('msg1')
session.flash('msg1', allow_duplicate=False)
self.assertEqual(session['_f_'], ['msg1'])
def test_flash_allow_duplicate_true_and_msg_not_in_storage(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session.flash('msg1', allow_duplicate=True)
self.assertEqual(session['_f_'], ['msg1'])
def test_flash_allow_duplicate_false_and_msg_not_in_storage(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session.flash('msg1', allow_duplicate=False)
self.assertEqual(session['_f_'], ['msg1'])
def test_flash_mixed(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session.flash('warn1', 'warn')
session.flash('warn2', 'warn')
session.flash('err1', 'error')
session.flash('err2', 'error')
self.assertEqual(session['_f_warn'], ['warn1', 'warn2'])
def test_pop_flash_default_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
queue = ['one', 'two']
session['_f_'] = queue
result = session.pop_flash()
self.assertEqual(result, queue)
self.assertEqual(session.get('_f_'), None)
def test_pop_flash_nodefault_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
queue = ['one', 'two']
session['_f_error'] = queue
result = session.pop_flash('error')
self.assertEqual(result, queue)
self.assertEqual(session.get('_f_error'), None)
def test_peek_flash_default_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
queue = ['one', 'two']
session['_f_'] = queue
result = session.peek_flash()
self.assertEqual(result, queue)
self.assertEqual(session.get('_f_'), queue)
def test_peek_flash_nodefault_queue(self):
request = testing.DummyRequest()
session = self._makeOne(request)
queue = ['one', 'two']
session['_f_error'] = queue
result = session.peek_flash('error')
self.assertEqual(result, queue)
self.assertEqual(session.get('_f_error'), queue)
def test_new_csrf_token(self):
request = testing.DummyRequest()
session = self._makeOne(request)
token = session.new_csrf_token()
self.assertEqual(token, session['_csrft_'])
def test_get_csrf_token(self):
request = testing.DummyRequest()
session = self._makeOne(request)
session['_csrft_'] = 'token'
token = session.get_csrf_token()
self.assertEqual(token, 'token')
self.assertTrue('_csrft_' in session)
def test_get_csrf_token_new(self):
request = testing.DummyRequest()
session = self._makeOne(request)
token = session.get_csrf_token()
self.assertTrue(token)
self.assertTrue('_csrft_' in session)
def test_serialize_option(self):
from pyramid.response import Response
secret = 'secret'
request = testing.DummyRequest()
session = self._makeOne(request,
signed_serialize=dummy_signed_serialize)
session['key'] = 'value'
response = Response()
self.assertEqual(session._set_cookie(response), True)
cookie = response.headerlist[-1][1]
expected_cookieval = dummy_signed_serialize(
(session.accessed, session.created, {'key': 'value'}), secret)
response = Response()
response.set_cookie('session', expected_cookieval)
expected_cookie = response.headerlist[-1][1]
self.assertEqual(cookie, expected_cookie)
def test_deserialize_option(self):
import time
secret = 'secret'
request = testing.DummyRequest()
accessed = time.time()
state = {'key': 'value'}
cookieval = dummy_signed_serialize((accessed, accessed, state), secret)
request.cookies['session'] = cookieval
session = self._makeOne(request,
signed_deserialize=dummy_signed_deserialize)
self.assertEqual(dict(session), state)
def dummy_signed_serialize(data, secret):
import base64
from pyramid.compat import pickle, bytes_
pickled = pickle.dumps(data)
return base64.b64encode(bytes_(secret)) + base64.b64encode(pickled)
def dummy_signed_deserialize(serialized, secret):
import base64
from pyramid.compat import pickle, bytes_
serialized_data = base64.b64decode(
serialized[len(base64.b64encode(bytes_(secret))):])
return pickle.loads(serialized_data)
class Test_manage_accessed(unittest.TestCase):
def _makeOne(self, wrapped):
from pyramid.session import manage_accessed
return manage_accessed(wrapped)
def test_accessed_set(self):
request = testing.DummyRequest()
session = DummySessionFactory(request)
session.accessed = None
wrapper = self._makeOne(session.__class__.get)
wrapper(session, 'a')
self.assertNotEqual(session.accessed, None)
def test_already_dirty(self):
request = testing.DummyRequest()
session = DummySessionFactory(request)
session._dirty = True
session['a'] = 1
wrapper = self._makeOne(session.__class__.get)
self.assertEqual(wrapper.__doc__, session.get.__doc__)
result = wrapper(session, 'a')
self.assertEqual(result, 1)
callbacks = request.response_callbacks
self.assertEqual(len(callbacks), 0)
def test_with_exception(self):
import webob
request = testing.DummyRequest()
request.exception = True
session = DummySessionFactory(request)
session['a'] = 1
wrapper = self._makeOne(session.__class__.get)
self.assertEqual(wrapper.__doc__, session.get.__doc__)
result = wrapper(session, 'a')
self.assertEqual(result, 1)
callbacks = request.response_callbacks
self.assertEqual(len(callbacks), 1)
response = webob.Response()
result = callbacks[0](request, response)
self.assertEqual(result, None)
self.assertFalse('Set-Cookie' in dict(response.headerlist))
def test_cookie_is_set(self):
request = testing.DummyRequest()
session = DummySessionFactory(request)
session['a'] = 1
wrapper = self._makeOne(session.__class__.get)
self.assertEqual(wrapper.__doc__, session.get.__doc__)
result = wrapper(session, 'a')
self.assertEqual(result, 1)
callbacks = request.response_callbacks
self.assertEqual(len(callbacks), 1)
response = DummyResponse()
result = callbacks[0](request, response)
self.assertEqual(result, None)
self.assertEqual(session.response, response)
def serialize(data, secret):
import hmac
import base64
from hashlib import sha1
from pyramid.compat import bytes_
from pyramid.compat import native_
from pyramid.compat import pickle
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest()
return sig + native_(base64.b64encode(pickled))
class Test_signed_serialize(unittest.TestCase):
def _callFUT(self, data, secret):
from pyramid.session import signed_serialize
return signed_serialize(data, secret)
def test_it(self):
expected = serialize('123', 'secret')
result = self._callFUT('123', 'secret')
self.assertEqual(result, expected)
class Test_signed_deserialize(unittest.TestCase):
def _callFUT(self, serialized, secret, hmac=None):
if hmac is None:
import hmac
from pyramid.session import signed_deserialize
return signed_deserialize(serialized, secret, hmac=hmac)
def test_it(self):
serialized = serialize('123', 'secret')
result = self._callFUT(serialized, 'secret')
self.assertEqual(result, '123')
def test_invalid_bits(self):
serialized = serialize('123', 'secret')
self.assertRaises(ValueError, self._callFUT, serialized, 'seekrit')
def test_invalid_len(self):
class hmac(object):
def new(self, *arg):
return self
def hexdigest(self):
return '1234'
serialized = serialize('123', 'secret123')
self.assertRaises(ValueError, self._callFUT, serialized, 'secret',
hmac=hmac())
def test_it_bad_encoding(self):
serialized = 'bad' + serialize('123', 'secret')
self.assertRaises(ValueError, self._callFUT, serialized, 'secret')
class Test_check_csrf_token(unittest.TestCase):
def _callFUT(self, request, token, raises=True):
from ..session import check_csrf_token
return check_csrf_token(request, token, raises=raises)
def test_success(self):
request = testing.DummyRequest()
request.params['csrf_token'] = request.session.get_csrf_token()
self.assertEqual(self._callFUT(request, 'csrf_token'), True)
def test_success_default_token(self):
from ..session import check_csrf_token
request = testing.DummyRequest()
request.params['csrf_token'] = request.session.get_csrf_token()
self.assertEqual(check_csrf_token(request), True)
def test_failure_raises(self):
from pyramid.httpexceptions import HTTPBadRequest
request = testing.DummyRequest()
self.assertRaises(HTTPBadRequest, self._callFUT, request, 'csrf_token')
def test_failure_no_raises(self):
request = testing.DummyRequest()
result = self._callFUT(request, 'csrf_token', raises=False)
self.assertEqual(result, False)
class DummySessionFactory(dict):
_dirty = False
_cookie_name = 'session'
_cookie_max_age = None
_cookie_path = '/'
_cookie_domain = None
_cookie_secure = False
_cookie_httponly = False
_timeout = 1200
_secret = 'secret'
def __init__(self, request):
self.request = request
dict.__init__(self, {})
def _set_cookie(self, response):
self.response = response
class DummyResponse(object):
def __init__(self):
self.headerlist = []
pyramid-1.4.5/pyramid/tests/test_request.py 0000664 0001750 0001750 00000052400 12210154177 020373 0 ustar takaki takaki import unittest
from pyramid import testing
from pyramid.compat import (
PY3,
text_,
bytes_,
native_,
iteritems_,
iterkeys_,
itervalues_,
)
class TestRequest(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
def _getTargetClass(self):
from pyramid.request import Request
return Request
def _makeOne(self, environ=None):
if environ is None:
environ = {}
return self._getTargetClass()(environ)
def _registerResourceURL(self):
from pyramid.interfaces import IResourceURL
from zope.interface import Interface
class DummyResourceURL(object):
def __init__(self, context, request):
self.physical_path = '/context/'
self.virtual_path = '/context/'
self.config.registry.registerAdapter(
DummyResourceURL, (Interface, Interface),
IResourceURL)
def test_class_conforms_to_IRequest(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import IRequest
verifyClass(IRequest, self._getTargetClass())
def test_instance_conforms_to_IRequest(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import IRequest
verifyObject(IRequest, self._makeOne())
def test_ResponseClass_is_pyramid_Response(self):
from pyramid.response import Response
cls = self._getTargetClass()
self.assertEqual(cls.ResponseClass, Response)
def test_charset_defaults_to_utf8(self):
r = self._makeOne({'PATH_INFO':'/'})
self.assertEqual(r.charset, 'UTF-8')
def test_exception_defaults_to_None(self):
r = self._makeOne({'PATH_INFO':'/'})
self.assertEqual(r.exception, None)
def test_matchdict_defaults_to_None(self):
r = self._makeOne({'PATH_INFO':'/'})
self.assertEqual(r.matchdict, None)
def test_matched_route_defaults_to_None(self):
r = self._makeOne({'PATH_INFO':'/'})
self.assertEqual(r.matched_route, None)
def test_params_decoded_from_utf_8_by_default(self):
environ = {
'PATH_INFO':'/',
'QUERY_STRING':'la=La%20Pe%C3%B1a'
}
request = self._makeOne(environ)
request.charset = None
self.assertEqual(request.GET['la'], text_(b'La Pe\xf1a'))
def test_tmpl_context(self):
from pyramid.request import TemplateContext
inst = self._makeOne()
result = inst.tmpl_context
self.assertEqual(result.__class__, TemplateContext)
def test_session_configured(self):
from pyramid.interfaces import ISessionFactory
inst = self._makeOne()
def factory(request):
return 'orangejuice'
self.config.registry.registerUtility(factory, ISessionFactory)
inst.registry = self.config.registry
self.assertEqual(inst.session, 'orangejuice')
self.assertEqual(inst.__dict__['session'], 'orangejuice')
def test_session_not_configured(self):
inst = self._makeOne()
inst.registry = self.config.registry
self.assertRaises(AttributeError, getattr, inst, 'session')
def test_setattr_and_getattr_dotnotation(self):
inst = self._makeOne()
inst.foo = 1
self.assertEqual(inst.foo, 1)
def test_setattr_and_getattr(self):
environ = {}
inst = self._makeOne(environ)
setattr(inst, 'bar', 1)
self.assertEqual(getattr(inst, 'bar'), 1)
self.assertEqual(environ, {}) # make sure we're not using adhoc attrs
def test_add_response_callback(self):
inst = self._makeOne()
self.assertEqual(inst.response_callbacks, ())
def callback(request, response):
""" """
inst.add_response_callback(callback)
self.assertEqual(inst.response_callbacks, [callback])
inst.add_response_callback(callback)
self.assertEqual(inst.response_callbacks, [callback, callback])
def test__process_response_callbacks(self):
inst = self._makeOne()
def callback1(request, response):
request.called1 = True
response.called1 = True
def callback2(request, response):
request.called2 = True
response.called2 = True
inst.response_callbacks = [callback1, callback2]
response = DummyResponse()
inst._process_response_callbacks(response)
self.assertEqual(inst.called1, True)
self.assertEqual(inst.called2, True)
self.assertEqual(response.called1, True)
self.assertEqual(response.called2, True)
self.assertEqual(inst.response_callbacks, [])
def test_add_finished_callback(self):
inst = self._makeOne()
self.assertEqual(inst.finished_callbacks, ())
def callback(request):
""" """
inst.add_finished_callback(callback)
self.assertEqual(inst.finished_callbacks, [callback])
inst.add_finished_callback(callback)
self.assertEqual(inst.finished_callbacks, [callback, callback])
def test__process_finished_callbacks(self):
inst = self._makeOne()
def callback1(request):
request.called1 = True
def callback2(request):
request.called2 = True
inst.finished_callbacks = [callback1, callback2]
inst._process_finished_callbacks()
self.assertEqual(inst.called1, True)
self.assertEqual(inst.called2, True)
self.assertEqual(inst.finished_callbacks, [])
def test_resource_url(self):
self._registerResourceURL()
environ = {
'PATH_INFO':'/',
'SERVER_NAME':'example.com',
'SERVER_PORT':'80',
'wsgi.url_scheme':'http',
}
inst = self._makeOne(environ)
root = DummyContext()
result = inst.resource_url(root)
self.assertEqual(result, 'http://example.com/context/')
def test_route_url(self):
environ = {
'PATH_INFO':'/',
'SERVER_NAME':'example.com',
'SERVER_PORT':'5432',
'QUERY_STRING':'la=La%20Pe%C3%B1a',
'wsgi.url_scheme':'http',
}
from pyramid.interfaces import IRoutesMapper
inst = self._makeOne(environ)
mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
self.config.registry.registerUtility(mapper, IRoutesMapper)
result = inst.route_url('flub', 'extra1', 'extra2',
a=1, b=2, c=3, _query={'a':1},
_anchor=text_("foo"))
self.assertEqual(result,
'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
def test_route_path(self):
environ = {
'PATH_INFO':'/',
'SERVER_NAME':'example.com',
'SERVER_PORT':'5432',
'QUERY_STRING':'la=La%20Pe%C3%B1a',
'wsgi.url_scheme':'http',
}
from pyramid.interfaces import IRoutesMapper
inst = self._makeOne(environ)
mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
self.config.registry.registerUtility(mapper, IRoutesMapper)
result = inst.route_path('flub', 'extra1', 'extra2',
a=1, b=2, c=3, _query={'a':1},
_anchor=text_("foo"))
self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo')
def test_static_url(self):
from pyramid.interfaces import IStaticURLInfo
environ = {
'PATH_INFO':'/',
'SERVER_NAME':'example.com',
'SERVER_PORT':'5432',
'QUERY_STRING':'',
'wsgi.url_scheme':'http',
}
request = self._makeOne(environ)
info = DummyStaticURLInfo('abc')
self.config.registry.registerUtility(info, IStaticURLInfo)
result = request.static_url('pyramid.tests:static/foo.css')
self.assertEqual(result, 'abc')
self.assertEqual(info.args,
('pyramid.tests:static/foo.css', request, {}) )
def test_is_response_false(self):
request = self._makeOne()
request.registry = self.config.registry
self.assertEqual(request.is_response('abc'), False)
def test_is_response_true_ob_is_pyramid_response(self):
from pyramid.response import Response
r = Response('hello')
request = self._makeOne()
request.registry = self.config.registry
self.assertEqual(request.is_response(r), True)
def test_is_response_false_adapter_is_not_self(self):
from pyramid.interfaces import IResponse
request = self._makeOne()
request.registry = self.config.registry
def adapter(ob):
return object()
class Foo(object):
pass
foo = Foo()
request.registry.registerAdapter(adapter, (Foo,), IResponse)
self.assertEqual(request.is_response(foo), False)
def test_is_response_adapter_true(self):
from pyramid.interfaces import IResponse
request = self._makeOne()
request.registry = self.config.registry
class Foo(object):
pass
foo = Foo()
def adapter(ob):
return ob
request.registry.registerAdapter(adapter, (Foo,), IResponse)
self.assertEqual(request.is_response(foo), True)
def test_json_body_invalid_json(self):
request = self._makeOne({'REQUEST_METHOD':'POST'})
request.body = b'{'
self.assertRaises(ValueError, getattr, request, 'json_body')
def test_json_body_valid_json(self):
request = self._makeOne({'REQUEST_METHOD':'POST'})
request.body = b'{"a":1}'
self.assertEqual(request.json_body, {'a':1})
def test_json_body_alternate_charset(self):
import json
request = self._makeOne({'REQUEST_METHOD':'POST'})
inp = text_(
b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf',
'utf-8'
)
if PY3: # pragma: no cover
body = bytes(json.dumps({'a':inp}), 'utf-16')
else:
body = json.dumps({'a':inp}).decode('utf-8').encode('utf-16')
request.body = body
request.content_type = 'application/json; charset=utf-16'
self.assertEqual(request.json_body, {'a':inp})
def test_json_body_GET_request(self):
request = self._makeOne({'REQUEST_METHOD':'GET'})
self.assertRaises(ValueError, getattr, request, 'json_body')
def test_set_property(self):
request = self._makeOne()
opts = [2, 1]
def connect(obj):
return opts.pop()
request.set_property(connect, name='db')
self.assertEqual(1, request.db)
self.assertEqual(2, request.db)
def test_set_property_reify(self):
request = self._makeOne()
opts = [2, 1]
def connect(obj):
return opts.pop()
request.set_property(connect, name='db', reify=True)
self.assertEqual(1, request.db)
self.assertEqual(1, request.db)
class TestRequestDeprecatedMethods(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
testing.tearDown()
from zope.deprecation import __show__
__show__.on()
def _getTargetClass(self):
from pyramid.request import Request
return Request
def _makeOne(self, environ=None):
if environ is None:
environ = {}
return self._getTargetClass()(environ)
def test___contains__(self):
environ ={'zooma':1}
inst = self._makeOne(environ)
self.assertTrue('zooma' in inst)
def test___delitem__(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
del inst['zooma']
self.assertFalse('zooma' in environ)
def test___getitem__(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(inst['zooma'], 1)
def test___iter__(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
iterator = iter(inst)
self.assertEqual(list(iterator), list(iter(environ)))
def test___setitem__(self):
environ = {}
inst = self._makeOne(environ)
inst['zooma'] = 1
self.assertEqual(environ, {'zooma':1})
def test_get(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(inst.get('zooma'), 1)
def test_has_key(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(inst.has_key('zooma'), True)
def test_items(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(inst.items(), environ.items())
def test_iteritems(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(list(inst.iteritems()), list(iteritems_(environ)))
def test_iterkeys(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(list(inst.iterkeys()), list(iterkeys_(environ)))
def test_itervalues(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(list(inst.itervalues()), list(itervalues_(environ)))
def test_keys(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
self.assertEqual(inst.keys(), environ.keys())
def test_pop(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
popped = inst.pop('zooma')
self.assertEqual(environ, {})
self.assertEqual(popped, 1)
def test_popitem(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
popped = inst.popitem()
self.assertEqual(environ, {})
self.assertEqual(popped, ('zooma', 1))
def test_setdefault(self):
environ = {}
inst = self._makeOne(environ)
marker = []
result = inst.setdefault('a', marker)
self.assertEqual(environ, {'a':marker})
self.assertEqual(result, marker)
def test_update(self):
environ = {}
inst = self._makeOne(environ)
inst.update({'a':1}, b=2)
self.assertEqual(environ, {'a':1, 'b':2})
def test_values(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
result = list(inst.values())
self.assertEqual(result, list(environ.values()))
def test_response_content_type(self):
inst = self._makeOne()
self.assertFalse(hasattr(inst, 'response_content_type'))
inst.response_content_type = 'abc'
self.assertEqual(inst.response_content_type, 'abc')
del inst.response_content_type
self.assertFalse(hasattr(inst, 'response_content_type'))
def test_response_headerlist(self):
inst = self._makeOne()
self.assertFalse(hasattr(inst, 'response_headerlist'))
inst.response_headerlist = 'abc'
self.assertEqual(inst.response_headerlist, 'abc')
del inst.response_headerlist
self.assertFalse(hasattr(inst, 'response_headerlist'))
def test_response_status(self):
inst = self._makeOne()
self.assertFalse(hasattr(inst, 'response_status'))
inst.response_status = 'abc'
self.assertEqual(inst.response_status, 'abc')
del inst.response_status
self.assertFalse(hasattr(inst, 'response_status'))
def test_response_charset(self):
inst = self._makeOne()
self.assertFalse(hasattr(inst, 'response_charset'))
inst.response_charset = 'abc'
self.assertEqual(inst.response_charset, 'abc')
del inst.response_charset
self.assertFalse(hasattr(inst, 'response_charset'))
def test_response_cache_for(self):
inst = self._makeOne()
self.assertFalse(hasattr(inst, 'response_cache_for'))
inst.response_cache_for = 'abc'
self.assertEqual(inst.response_cache_for, 'abc')
del inst.response_cache_for
self.assertFalse(hasattr(inst, 'response_cache_for'))
class Test_route_request_iface(unittest.TestCase):
def _callFUT(self, name):
from pyramid.request import route_request_iface
return route_request_iface(name)
def test_it(self):
iface = self._callFUT('routename')
self.assertEqual(iface.__name__, 'routename_IRequest')
self.assertTrue(hasattr(iface, 'combined'))
self.assertEqual(iface.combined.__name__, 'routename_combined_IRequest')
def test_it_routename_with_spaces(self):
# see https://github.com/Pylons/pyramid/issues/232
iface = self._callFUT('routename with spaces')
self.assertEqual(iface.__name__, 'routename with spaces_IRequest')
self.assertTrue(hasattr(iface, 'combined'))
self.assertEqual(iface.combined.__name__,
'routename with spaces_combined_IRequest')
class Test_add_global_response_headers(unittest.TestCase):
def _callFUT(self, request, headerlist):
from pyramid.request import add_global_response_headers
return add_global_response_headers(request, headerlist)
def test_it(self):
request = DummyRequest()
response = DummyResponse()
self._callFUT(request, [('c', 1)])
self.assertEqual(len(request.response_callbacks), 1)
request.response_callbacks[0](None, response)
self.assertEqual(response.headerlist, [('c', 1)] )
class Test_call_app_with_subpath_as_path_info(unittest.TestCase):
def _callFUT(self, request, app):
from pyramid.request import call_app_with_subpath_as_path_info
return call_app_with_subpath_as_path_info(request, app)
def test_it_all_request_and_environment_data_missing(self):
request = DummyRequest({})
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
self.assertEqual(request.environ['SCRIPT_NAME'], '')
self.assertEqual(request.environ['PATH_INFO'], '/')
def test_it_with_subpath_and_path_info(self):
request = DummyRequest({'PATH_INFO':'/hello'})
request.subpath = ('hello',)
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
self.assertEqual(request.environ['SCRIPT_NAME'], '')
self.assertEqual(request.environ['PATH_INFO'], '/hello')
def test_it_with_subpath_and_path_info_path_info_endswith_slash(self):
request = DummyRequest({'PATH_INFO':'/hello/'})
request.subpath = ('hello',)
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
self.assertEqual(request.environ['SCRIPT_NAME'], '')
self.assertEqual(request.environ['PATH_INFO'], '/hello/')
def test_it_with_subpath_and_path_info_extra_script_name(self):
request = DummyRequest({'PATH_INFO':'/hello', 'SCRIPT_NAME':'/script'})
request.subpath = ('hello',)
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
self.assertEqual(request.environ['SCRIPT_NAME'], '/script')
self.assertEqual(request.environ['PATH_INFO'], '/hello')
def test_it_with_extra_slashes_in_path_info(self):
request = DummyRequest({'PATH_INFO':'//hello/',
'SCRIPT_NAME':'/script'})
request.subpath = ('hello',)
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
self.assertEqual(request.environ['SCRIPT_NAME'], '/script')
self.assertEqual(request.environ['PATH_INFO'], '/hello/')
def test_subpath_path_info_and_script_name_have_utf8(self):
encoded = native_(text_(b'La Pe\xc3\xb1a'))
decoded = text_(bytes_(encoded), 'utf-8')
request = DummyRequest({'PATH_INFO':'/' + encoded,
'SCRIPT_NAME':'/' + encoded})
request.subpath = (decoded, )
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded)
self.assertEqual(request.environ['PATH_INFO'], '/' + encoded)
class DummyRequest:
def __init__(self, environ=None):
if environ is None:
environ = {}
self.environ = environ
def add_response_callback(self, callback):
self.response_callbacks = [callback]
def get_response(self, app):
return app
def copy(self):
self.copied = True
return self
class DummyResponse:
def __init__(self):
self.headerlist = []
class DummyContext:
pass
class DummyRoutesMapper:
raise_exc = None
def __init__(self, route=None, raise_exc=False):
self.route = route
def get_route(self, route_name):
return self.route
class DummyRoute:
pregenerator = None
def __init__(self, result='/1/2/3'):
self.result = result
def generate(self, kw):
self.kw = kw
return self.result
class DummyStaticURLInfo:
def __init__(self, result):
self.result = result
def generate(self, path, request, **kw):
self.args = path, request, kw
return self.result
pyramid-1.4.5/pyramid/tests/test_location.py 0000664 0001750 0001750 00000002501 12203712502 020502 0 ustar takaki takaki import unittest
class TestInside(unittest.TestCase):
def _callFUT(self, one, two):
from pyramid.location import inside
return inside(one, two)
def test_inside(self):
o1 = Location()
o2 = Location(); o2.__parent__ = o1
o3 = Location(); o3.__parent__ = o2
o4 = Location(); o4.__parent__ = o3
self.assertEqual(self._callFUT(o1, o1), True)
self.assertEqual(self._callFUT(o2, o1), True)
self.assertEqual(self._callFUT(o3, o1), True)
self.assertEqual(self._callFUT(o4, o1), True)
self.assertEqual(self._callFUT(o1, o4), False)
self.assertEqual(self._callFUT(o1, None), False)
class TestLineage(unittest.TestCase):
def _callFUT(self, context):
from pyramid.location import lineage
return lineage(context)
def test_lineage(self):
o1 = Location()
o2 = Location(); o2.__parent__ = o1
o3 = Location(); o3.__parent__ = o2
o4 = Location(); o4.__parent__ = o3
result = list(self._callFUT(o3))
self.assertEqual(result, [o3, o2, o1])
result = list(self._callFUT(o1))
self.assertEqual(result, [o1])
from pyramid.interfaces import ILocation
from zope.interface import implementer
@implementer(ILocation)
class Location(object):
__name__ = __parent__ = None
pyramid-1.4.5/pyramid/tests/pkgs/ 0000775 0001750 0001750 00000000000 12210157153 016232 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/restbugapp/ 0000775 0001750 0001750 00000000000 12210157153 020406 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/restbugapp/__init__.py 0000664 0001750 0001750 00000001213 12203712502 022511 0 ustar takaki takaki def includeme(config):
config.add_route('gameactions_pet_get_pets', '/pet',
request_method='GET')
config.add_route('gameactions_pet_care_for_pet', '/pet',
request_method='POST')
config.add_view('.views.PetRESTView',
route_name='gameactions_pet_get_pets',
attr='GET',
permission='view',
renderer='json')
config.add_view('.views.PetRESTView',
route_name='gameactions_pet_care_for_pet',
attr='POST',
permission='view',
renderer='json')
pyramid-1.4.5/pyramid/tests/pkgs/restbugapp/views.py 0000664 0001750 0001750 00000000661 12203712502 022115 0 ustar takaki takaki from pyramid.response import Response
class BaseRESTView(object):
def __init__(self, context, request):
self.context = context
self.request = request
class PetRESTView(BaseRESTView):
""" REST Controller to control action of an avatar """
def __init__(self, context, request):
super(PetRESTView, self).__init__(context, request)
def GET(self):
return Response('gotten')
pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/ 0000775 0001750 0001750 00000000000 12210157153 021610 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/__init__.py 0000664 0001750 0001750 00000000123 12203712502 023712 0 ustar takaki takaki def includeme(config):
config.scan('pyramid.tests.pkgs.viewdecoratorapp')
pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/ 0000775 0001750 0001750 00000000000 12210157153 022745 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/__init__.py 0000664 0001750 0001750 00000000012 12203712502 025044 0 ustar takaki takaki # package
pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/templates/ 0000775 0001750 0001750 00000000000 12210157153 024743 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/templates/foo.pt 0000664 0001750 0001750 00000000031 12203712502 026062 0 ustar takaki takaki
${result}
pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/views.py 0000664 0001750 0001750 00000000466 12203712502 024457 0 ustar takaki takaki from pyramid.view import view_config
@view_config(renderer='templates/foo.pt', name='first')
def first(request):
return {'result':'OK1'}
@view_config(
renderer='pyramid.tests.pkgs.viewdecoratorapp.views:templates/foo.pt',
name='second')
def second(request):
return {'result':'OK2'}
pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/ 0000775 0001750 0001750 00000000000 12210157153 021406 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/two/ 0000775 0001750 0001750 00000000000 12210157153 022217 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/two/two.pt 0000664 0001750 0001750 00000000165 12203712502 023374 0 ustar takaki takaki
${nameagain}
pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/two/__init__.py 0000664 0001750 0001750 00000000205 12203712502 024322 0 ustar takaki takaki from pyramid.view import view_config
@view_config(name='two', renderer='two.pt')
def two(request):
return {'nameagain':'Two!'}
pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/__init__.py 0000664 0001750 0001750 00000000256 12203712502 023517 0 ustar takaki takaki from pyramid.view import view_config
@view_config(name='one', renderer='one.pt')
def one(request):
return {'name':'One!'}
def includeme(config):
config.scan()
pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/one.pt 0000664 0001750 0001750 00000000161 12203712502 022527 0 ustar takaki takaki
${name}
pyramid-1.4.5/pyramid/tests/pkgs/static_abspath/ 0000775 0001750 0001750 00000000000 12210157153 021223 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/static_abspath/__init__.py 0000664 0001750 0001750 00000000306 12203712502 023330 0 ustar takaki takaki import os
def includeme(config):
here = here = os.path.dirname(__file__)
fixtures = os.path.normpath(os.path.join(here, '..', '..', 'fixtures'))
config.add_static_view('/', fixtures)
pyramid-1.4.5/pyramid/tests/pkgs/hybridapp/ 0000775 0001750 0001750 00000000000 12210157153 020214 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/hybridapp/__init__.py 0000664 0001750 0001750 00000004107 12203712502 022324 0 ustar takaki takaki def includeme(config):
#
config.add_route('route', 'abc')
config.add_view('.views.route_view', route_name='route')
#
config.add_view('.views.global_view',
context='pyramid.traversal.DefaultRootFactory')
config.add_view('.views.global2_view',
context='pyramid.traversal.DefaultRootFactory',
name='global2')
config.add_route('route2', 'def')
#
config.add_view('.views.route2_view', route_name='route2')
#
config.add_route('route3', 'ghi', use_global_views=True)
#
config.add_route('route4', 'jkl')
#
config.add_route('route5', 'mno/*traverse')
#
config.add_route('route6', 'pqr/*traverse', use_global_views=True)
config.add_route('route7', 'error')
config.add_view('.views.erroneous_view', route_name='route7')
config.add_route('route8', 'error2')
config.add_view('.views.erroneous_view', route_name='route8')
#
config.add_view('.views.exception_view', context=RuntimeError)
#
config.add_view('.views.exception2_view', context=RuntimeError,
route_name='route8')
config.add_route('route9', 'error_sub')
config.add_view('.views.erroneous_sub_view', route_name='route9')
#
config.add_view('.views.exception2_view', context='.views.SuperException',
route_name='route9')
#
config.add_view('.views.exception_view', context='.views.SubException')
pyramid-1.4.5/pyramid/tests/pkgs/hybridapp/views.py 0000664 0001750 0001750 00000001260 12203712502 021717 0 ustar takaki takaki from webob import Response
def route_view(request):
""" """
return Response('route')
def global_view(request):
""" """
return Response('global')
def global2_view(request):
""" """
return Response('global2')
def route2_view(request):
""" """
return Response('route2')
def exception_view(request):
""" """
return Response('supressed')
def exception2_view(request):
""" """
return Response('supressed2')
def erroneous_view(request):
""" """
raise RuntimeError()
def erroneous_sub_view(request):
""" """
raise SubException()
class SuperException(Exception):
""" """
class SubException(SuperException):
""" """
pyramid-1.4.5/pyramid/tests/pkgs/wsgiapp2app/ 0000775 0001750 0001750 00000000000 12210157153 020467 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/wsgiapp2app/__init__.py 0000664 0001750 0001750 00000000741 12203712502 022577 0 ustar takaki takaki from pyramid.view import view_config
from pyramid.wsgi import wsgiapp2
@view_config(name='hello', renderer='string')
@wsgiapp2
def hello(environ, start_response):
assert environ['PATH_INFO'] == '/'
assert environ['SCRIPT_NAME'] == '/hello'
response_headers = [('Content-Type', 'text/plain')]
start_response('200 OK', response_headers)
return [b'Hello!']
def main():
from pyramid.config import Configurator
c = Configurator()
c.scan()
return c
pyramid-1.4.5/pyramid/tests/pkgs/staticpermapp/ 0000775 0001750 0001750 00000000000 12210157153 021106 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/staticpermapp/__init__.py 0000664 0001750 0001750 00000001757 12203712502 023226 0 ustar takaki takaki class RootFactory(object):
__acl__ = [('Allow', 'fred', 'view')]
def __init__(self, request):
pass
class LocalRootFactory(object):
__acl__ = [('Allow', 'bob', 'view')]
def __init__(self, request):
pass
def includeme(config):
from pyramid.authentication import RemoteUserAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
authn_policy = RemoteUserAuthenticationPolicy()
authz_policy = ACLAuthorizationPolicy()
config._set_authentication_policy(authn_policy)
config._set_authorization_policy(authz_policy)
config.add_static_view('allowed', 'pyramid.tests:fixtures/static/')
config.add_static_view('protected', 'pyramid.tests:fixtures/static/',
permission='view')
config.add_static_view('factory_protected',
'pyramid.tests:fixtures/static/',
permission='view',
factory=LocalRootFactory)
pyramid-1.4.5/pyramid/tests/pkgs/localeapp/ 0000775 0001750 0001750 00000000000 12210157153 020172 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/ 0000775 0001750 0001750 00000000000 12210157153 021514 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/ 0000775 0001750 0001750 00000000000 12210157152 022115 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/ 0000775 0001750 0001750 00000000000 12210157153 023703 5 ustar takaki takaki pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.po 0000664 0001750 0001750 00000001516 12203712502 026404 0 ustar takaki takaki # German translations for deformsite.
# Copyright (C) 2010 ORGANIZATION
# This file is distributed under the same license as the deformsite project.
# FIRST AUTHOR , 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: deformsite 0.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2010-04-22 14:17+0400\n"
"PO-Revision-Date: 2010-04-22 14:17-0400\n"
"Last-Translator: FULL NAME \n"
"Language-Team: de \n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.5\n"
#: deformsite/__init__.py:458
msgid "Approve"
msgstr "Genehmigen"
#: deformsite/__init__.py:459
msgid "Show approval"
msgstr "Zeigen Genehmigung"
#: deformsite/__init__.py:466
msgid "Submit"
msgstr "Beugen"
pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.mo 0000664 0001750 0001750 00000001037 12203712502 026377 0 ustar takaki takaki Þ• <