pyramid-1.4.5/0000775000175000017500000000000012212235513012456 5ustar takakitakakipyramid-1.4.5/CHANGES.txt0000664000175000017500000010160612210156141014270 0ustar takakitakaki1.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.txt0000664000175000017500000000005012203712502014001 0ustar takakitakakirepoze.sphinx.autointerface repoze.lru pyramid-1.4.5/pyramid.egg-info/0000775000175000017500000000000012210157153015616 5ustar takakitakakipyramid-1.4.5/pyramid.egg-info/top_level.txt0000644000175000017500000000001012210157112020330 0ustar takakitakakipyramid pyramid-1.4.5/pyramid.egg-info/requires.txt0000644000175000017500000000047612210157112020216 0ustar takakitakakisetuptools 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 virtualenvpyramid-1.4.5/pyramid.egg-info/entry_points.txt0000644000175000017500000000137512210157112021113 0ustar takakitakaki [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.txt0000644000175000017500000007526612210157152017517 0ustar takakitakaki.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.pypyramid-1.4.5/pyramid.egg-info/not-zip-safe0000644000175000017500000000000111701326317020046 0ustar takakitakaki pyramid-1.4.5/pyramid.egg-info/PKG-INFO0000644000175000017500000012162012210157112016706 0ustar takakitakakiMetadata-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.txt0000644000175000017500000000000112210157112021655 0ustar takakitakaki pyramid-1.4.5/RELEASING.txt0000664000175000017500000000401012203712502014521 0ustar takakitakakiReleasing 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.rst0000664000175000017500000000131612210154276014152 0ustar takakitakakiPyramid ======= 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.yml0000664000175000017500000000023212210154276014570 0ustar takakitakakilanguage: 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/.gitignore0000664000175000017500000000033412210154276014452 0ustar takakitakaki*.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.txt0000664000175000017500000001352412210154276013775 0ustar takakitakakiPyramid 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.ini0000664000175000017500000000077212203712502013775 0ustar takakitakaki[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.cfg0000664000175000017500000000043412210157153014301 0ustar takakitakaki[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/0000775000175000017500000000000012210157153014124 5ustar takakitakakipyramid-1.4.5/pyramid/renderers.py0000664000175000017500000005657512210154276016514 0ustar takakitakakiimport 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.py0000664000175000017500000001253512203712502016503 0ustar takakitakakifrom 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.py0000664000175000017500000010550412210154301016476 0ustar takakitakakiimport 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.py0000664000175000017500000012135712210154276017531 0ustar takakitakakiimport 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.py0000664000175000017500000003662212210154276015446 0ustar takakitakakiimport 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.py0000664000175000017500000000234312210154276017502 0ustar takakitakakifrom 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.py0000664000175000017500000001522212203712502015760 0ustar takakitakakiimport 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/0000775000175000017500000000000012210157153015371 5ustar takakitakakipyramid-1.4.5/pyramid/config/predicates.py0000664000175000017500000002023612210154276020074 0ustar takakitakakiimport 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.py0000664000175000017500000003301112210154276017547 0ustar takakitakakifrom 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.py0000664000175000017500000001703212210154276017730 0ustar takakitakakifrom 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.py0000664000175000017500000001614512210154276017432 0ustar takakitakakifrom 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.py0000664000175000017500000001001112210154276017714 0ustar takakitakakiimport 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.py0000664000175000017500000001204212203712502016516 0ustar takakitakakiimport 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.py0000664000175000017500000001247712210154276016736 0ustar takakitakakifrom 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.py0000664000175000017500000002055612210154276017263 0ustar takakitakakifrom 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__.py0000664000175000017500000014060012210154276017506 0ustar takakitakakiimport 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.py0000664000175000017500000005747612210154276017312 0ustar takakitakakiimport 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.py0000664000175000017500000023524012210154276017111 0ustar takakitakakiimport 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.py0000664000175000017500000002442012203712502017244 0ustar takakitakakiimport 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.py0000664000175000017500000000160112203712502016513 0ustar takakitakakifrom 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.py0000664000175000017500000001524712203712502017620 0ustar takakitakakifrom 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.py0000664000175000017500000001656512203712502017615 0ustar takakitakakiimport 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.py0000664000175000017500000001167412210154276016010 0ustar takakitakakiimport 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.py0000664000175000017500000001312212203712502015761 0ustar takakitakaki# -*- 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.py0000664000175000017500000001376712210154276017417 0ustar takakitakakifrom 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/0000775000175000017500000000000012210157153015266 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_util.py0000664000175000017500000005174212203712502017662 0ustar takakitakakiimport 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.py0000664000175000017500000000462612203712502020544 0ustar takakitakakiimport 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/0000775000175000017500000000000012210157153020014 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scripts/test_pshell.py0000664000175000017500000003365012203712502022720 0ustar takakitakakiimport 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.py0000664000175000017500000001431712203712502023131 0ustar takakitakakiimport 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.py0000664000175000017500000000245012203712502022713 0ustar takakitakakiimport 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.py0000664000175000017500000001031212203712502023042 0ustar takakitakakiimport 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.py0000664000175000017500000000762512203712502021530 0ustar takakitakakiclass 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.py0000664000175000017500000001340412210154276023302 0ustar takakitakakiimport 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.py0000664000175000017500000002565112210154276022745 0ustar takakitakakiimport 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__.py0000664000175000017500000000001212203712502022113 0ustar takakitakaki# package pyramid-1.4.5/pyramid/tests/test_scripts/test_ptweens.py0000664000175000017500000000373412203712502023116 0ustar takakitakakiimport 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.py0000664000175000017500000005014012203712502022737 0ustar takakitakakiimport 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.py0000664000175000017500000002235612210154276021612 0ustar takakitakakiimport 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.py0000664000175000017500000007213512203712502017656 0ustar takakitakakiimport 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.py0000664000175000017500000000613312203712502020016 0ustar takakitakakiimport 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.py0000664000175000017500000000373712203712502020143 0ustar takakitakakiimport 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.py0000664000175000017500000003565012210154276020376 0ustar takakitakakiimport 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.py0000664000175000017500000005240012210154177020373 0ustar takakitakakiimport 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.py0000664000175000017500000000250112203712502020502 0ustar takakitakakiimport 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/0000775000175000017500000000000012210157153016232 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/restbugapp/0000775000175000017500000000000012210157153020406 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/restbugapp/__init__.py0000664000175000017500000000121312203712502022511 0ustar takakitakakidef 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.py0000664000175000017500000000066112203712502022115 0ustar takakitakakifrom 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/0000775000175000017500000000000012210157153021610 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/__init__.py0000664000175000017500000000012312203712502023712 0ustar takakitakakidef includeme(config): config.scan('pyramid.tests.pkgs.viewdecoratorapp') pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/0000775000175000017500000000000012210157153022745 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/__init__.py0000664000175000017500000000001212203712502025044 0ustar takakitakaki# package pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/templates/0000775000175000017500000000000012210157153024743 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/templates/foo.pt0000664000175000017500000000003112203712502026062 0ustar takakitakaki ${result} pyramid-1.4.5/pyramid/tests/pkgs/viewdecoratorapp/views/views.py0000664000175000017500000000046612203712502024457 0ustar takakitakakifrom 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/0000775000175000017500000000000012210157153021406 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/two/0000775000175000017500000000000012210157153022217 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/two/two.pt0000664000175000017500000000016512203712502023374 0ustar takakitakaki
${nameagain}
pyramid-1.4.5/pyramid/tests/pkgs/rendererscanapp/two/__init__.py0000664000175000017500000000020512203712502024322 0ustar takakitakakifrom 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__.py0000664000175000017500000000025612203712502023517 0ustar takakitakakifrom 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.pt0000664000175000017500000000016112203712502022527 0ustar takakitakaki
${name}
pyramid-1.4.5/pyramid/tests/pkgs/static_abspath/0000775000175000017500000000000012210157153021223 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/static_abspath/__init__.py0000664000175000017500000000030612203712502023330 0ustar takakitakakiimport 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/0000775000175000017500000000000012210157153020214 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/hybridapp/__init__.py0000664000175000017500000000410712203712502022324 0ustar takakitakakidef 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.py0000664000175000017500000000126012203712502021717 0ustar takakitakakifrom 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/0000775000175000017500000000000012210157153020467 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/wsgiapp2app/__init__.py0000664000175000017500000000074112203712502022577 0ustar takakitakakifrom 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/0000775000175000017500000000000012210157153021106 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/staticpermapp/__init__.py0000664000175000017500000000175712203712502023226 0ustar takakitakakiclass 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/0000775000175000017500000000000012210157153020172 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/0000775000175000017500000000000012210157153021514 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/0000775000175000017500000000000012210157152022115 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/0000775000175000017500000000000012210157153023703 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.po0000664000175000017500000000151612203712502026404 0ustar takakitakaki# 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.mo0000664000175000017500000000103712203712502026377 0ustar takakitakakiÞ•<\] esz úApproveShow approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:19-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 GenehmigenZeigen GenehmigungBeugenpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/de/0000775000175000017500000000000012210157152022103 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/0000775000175000017500000000000012210157153023671 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.po0000664000175000017500000000151612203712502026372 0ustar takakitakaki# 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/de/LC_MESSAGES/deformsite.mo0000664000175000017500000000103712203712502026365 0ustar takakitakakiÞ•<\] esz úApproveShow approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:19-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 GenehmigenZeigen GenehmigungBeugenpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/GARBAGE0000664000175000017500000000001612203712502022461 0ustar takakitakakiGarbage file. pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/be/0000775000175000017500000000000012210157153022102 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale3/be/LC_MESSAGES0000664000175000017500000000001012203712502023576 0ustar takakitakakibusted. pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/0000775000175000017500000000000012210157153021431 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/en/0000775000175000017500000000000012210157152022032 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/en/LC_MESSAGES/0000775000175000017500000000000012210157153023620 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.po0000664000175000017500000000151612203712502026321 0ustar takakitakaki# 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/locale/en/LC_MESSAGES/deformsite.mo0000664000175000017500000000103712203712502026314 0ustar takakitakakiÞ•<\] esz úApproveShow approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:19-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 GenehmigenZeigen GenehmigungBeugenpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de_DE/0000775000175000017500000000000012210157152022370 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/0000775000175000017500000000000012210157153024156 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.po0000664000175000017500000000141512203712502026655 0ustar takakitakaki# 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:459 msgid "Show approval" msgstr "Zeigen Genehmigung" #: deformsite/__init__.py:466 msgid "Submit" msgstr "different" pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.mo0000664000175000017500000000102312203712502026645 0ustar takakitakakiÞ•4L` aovö Show approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:17-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 Zeigen Genehmigungdifferentpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de/0000775000175000017500000000000012210157152022020 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de/LC_MESSAGES/0000775000175000017500000000000012210157153023606 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.po0000664000175000017500000000151612203712502026307 0ustar takakitakaki# 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/locale/de/LC_MESSAGES/deformsite.mo0000664000175000017500000000103712203712502026302 0ustar takakitakakiÞ•<\] esz úApproveShow approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:19-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 GenehmigenZeigen GenehmigungBeugenpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/GARBAGE0000664000175000017500000000001612203712502022376 0ustar takakitakakiGarbage file. pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/be/0000775000175000017500000000000012210157153022017 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale/be/LC_MESSAGES0000664000175000017500000000001012203712502023513 0ustar takakitakakibusted. pyramid-1.4.5/pyramid/tests/pkgs/localeapp/__init__.py0000664000175000017500000000001112203712502022270 0ustar takakitakaki# a file pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/0000775000175000017500000000000012210157153021513 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/en/0000775000175000017500000000000012210157152022114 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/0000775000175000017500000000000012210157153023702 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.po0000664000175000017500000000151612203712502026403 0ustar takakitakaki# 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/locale2/en/LC_MESSAGES/deformsite.mo0000664000175000017500000000103712203712502026376 0ustar takakitakakiÞ•<\] esz úApproveShow approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:19-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 GenehmigenZeigen GenehmigungBeugenpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/de/0000775000175000017500000000000012210157152022102 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/0000775000175000017500000000000012210157153023670 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.po0000664000175000017500000000151612203712502026371 0ustar takakitakaki# 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/locale2/de/LC_MESSAGES/deformsite.mo0000664000175000017500000000103712203712502026364 0ustar takakitakakiÞ•<\] esz úApproveShow approvalSubmitProject-Id-Version: deformsite 0.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-04-22 14:17+0400 PO-Revision-Date: 2010-04-22 14:19-0400 Last-Translator: FULL NAME Language-Team: de Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.5 GenehmigenZeigen GenehmigungBeugenpyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/GARBAGE0000664000175000017500000000001612203712502022460 0ustar takakitakakiGarbage file. pyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/be/0000775000175000017500000000000012210157153022101 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/localeapp/locale2/be/LC_MESSAGES0000664000175000017500000000001012203712502023575 0ustar takakitakakibusted. pyramid-1.4.5/pyramid/tests/pkgs/static_routeprefix/0000775000175000017500000000000012210157153022155 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/static_routeprefix/__init__.py0000664000175000017500000000035712203712502024270 0ustar takakitakakidef includeme(config): config.add_static_view('/static', 'pyramid.tests:fixtures') config.include(includeme2, route_prefix='/prefix') def includeme2(config): config.add_static_view('/static', 'pyramid.tests:fixtures/static') pyramid-1.4.5/pyramid/tests/pkgs/eventonly/0000775000175000017500000000000012210157153020255 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/eventonly/__init__.py0000664000175000017500000000266512203712502022374 0ustar takakitakakifrom pyramid.view import view_config from pyramid.events import subscriber class Yup(object): def __init__(self, val, config): self.val = val def text(self): return 'path_startswith = %s' % (self.val,) phash = text def __call__(self, event): return getattr(event.response, 'yup', False) class Foo(object): def __init__(self, response): self.response = response class Bar(object): pass @subscriber(Foo) def foo(event): event.response.text += 'foo ' @subscriber(Foo, yup=True) def fooyup(event): event.response.text += 'fooyup ' @subscriber([Foo, Bar]) def foobar(event): event.response.text += 'foobar ' @subscriber([Foo, Bar]) def foobar2(event, context): event.response.text += 'foobar2 ' @subscriber([Foo, Bar], yup=True) def foobaryup(event): event.response.text += 'foobaryup ' @subscriber([Foo, Bar], yup=True) def foobaryup2(event, context): event.response.text += 'foobaryup2 ' @view_config(name='sendfoo') def sendfoo(request): response = request.response response.yup = True request.registry.notify(Foo(response)) return response @view_config(name='sendfoobar') def sendfoobar(request): response = request.response response.yup = True request.registry.notify(Foo(response), Bar()) return response def includeme(config): config.add_subscriber_predicate('yup', Yup) config.scan('pyramid.tests.pkgs.eventonly') pyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/0000775000175000017500000000000012210157153020421 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/models.py0000664000175000017500000000014712203712502022255 0ustar takakitakakifrom zope.interface import Interface class IFixture(Interface): pass def fixture(): """ """ pyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/__init__.py0000664000175000017500000000104412203712502022526 0ustar takakitakakidef includeme(config): config.add_view('.views.fixture_view') config.add_view('.views.exception_view', context=RuntimeError) config.add_view('.views.protected_view', name='protected.html') config.add_view('.views.erroneous_view', name='error.html') config.add_view('.views.fixture_view', name='dummyskin.html', request_type='.views.IDummy') from .models import fixture, IFixture config.registry.registerUtility(fixture, IFixture) config.add_view('.views.fixture_view', name='another.html') pyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/templates/0000775000175000017500000000000012210157153022417 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/templates/fixture.pt0000664000175000017500000000020512203712502024444 0ustar takakitakaki pyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/views.py0000664000175000017500000000072112203712502022125 0ustar takakitakakifrom zope.interface import Interface from webob import Response from pyramid.httpexceptions import HTTPForbidden def fixture_view(context, request): """ """ return Response('fixture') def erroneous_view(context, request): """ """ raise RuntimeError() def exception_view(context, request): """ """ return Response('supressed') def protected_view(context, request): """ """ raise HTTPForbidden() class IDummy(Interface): pass pyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/subpackage/0000775000175000017500000000000012210157153022526 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/subpackage/__init__.py0000664000175000017500000000001112203712502024624 0ustar takakitakaki#package pyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/subpackage/templates/0000775000175000017500000000000012210157153024524 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/fixtureapp/subpackage/templates/bar.pt0000664000175000017500000000001712203712502025630 0ustar takakitakaki pyramid-1.4.5/pyramid/tests/pkgs/conflictapp/0000775000175000017500000000000012210157153020534 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/conflictapp/included.py0000664000175000017500000000017512203712502022675 0ustar takakitakakifrom webob import Response def bview(request): return Response('b view') def includeme(config): config.add_view(bview) pyramid-1.4.5/pyramid/tests/pkgs/conflictapp/__init__.py0000664000175000017500000000156512203712502022651 0ustar takakitakakifrom pyramid.response import Response from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy def aview(request): return Response('a view') def routeview(request): return Response('route view') def protectedview(request): return Response('protected view') def includeme(config): # purposely sorta-randomly ordered (route comes after view naming it, # authz comes after views) config.add_view(aview) config.add_view(protectedview, name='protected', permission='view') config.add_view(routeview, route_name='aroute') config.add_route('aroute', '/route') config.set_authentication_policy(AuthTktAuthenticationPolicy( 'seekri1t', hashalg='sha512')) config.set_authorization_policy(ACLAuthorizationPolicy()) config.include('pyramid.tests.pkgs.conflictapp.included') pyramid-1.4.5/pyramid/tests/pkgs/exceptionviewapp/0000775000175000017500000000000012210157153021624 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/exceptionviewapp/models.py0000664000175000017500000000040712203712502023457 0ustar takakitakaki class NotAnException(object): pass class AnException(Exception): pass class RouteContext(object): pass class RouteContext2(object): pass def route_factory(*arg): return RouteContext() def route_factory2(*arg): return RouteContext2() pyramid-1.4.5/pyramid/tests/pkgs/exceptionviewapp/__init__.py0000664000175000017500000000316612210154301023733 0ustar takakitakakifrom pyramid.httpexceptions import HTTPException def includeme(config): config.add_route('route_raise_exception', 'route_raise_exception') config.add_route('route_raise_httpexception', 'route_raise_httpexception') config.add_route('route_raise_exception2', 'route_raise_exception2', factory='.models.route_factory') config.add_route('route_raise_exception3', 'route_raise_exception3', factory='.models.route_factory2') config.add_route('route_raise_exception4', 'route_raise_exception4') config.add_view('.views.maybe') config.add_view('.views.no', context='.models.NotAnException') config.add_view('.views.yes', context=".models.AnException") config.add_view('.views.raise_exception', name='raise_exception') config.add_view('.views.raise_exception', route_name='route_raise_exception') config.add_view('.views.raise_exception', route_name='route_raise_exception2') config.add_view('.views.raise_exception', route_name='route_raise_exception3') config.add_view('.views.whoa', context='.models.AnException', route_name='route_raise_exception3') config.add_view('.views.raise_exception', route_name='route_raise_exception4') config.add_view('.views.whoa', context='.models.AnException', route_name='route_raise_exception4') config.add_view('.views.raise_httpexception', route_name='route_raise_httpexception') config.add_view('.views.catch_httpexception', context=HTTPException) pyramid-1.4.5/pyramid/tests/pkgs/exceptionviewapp/views.py0000664000175000017500000000074112210154301023325 0ustar takakitakakifrom webob import Response from .models import AnException from pyramid.httpexceptions import HTTPBadRequest def no(request): return Response('no') def yes(request): return Response('yes') def maybe(request): return Response('maybe') def whoa(request): return Response('whoa') def raise_exception(request): raise AnException() def raise_httpexception(request): raise HTTPBadRequest def catch_httpexception(request): return Response('caught') pyramid-1.4.5/pyramid/tests/pkgs/__init__.py0000664000175000017500000000001212203712502020331 0ustar takakitakaki# package pyramid-1.4.5/pyramid/tests/pkgs/permbugapp/0000775000175000017500000000000012210157153020374 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/permbugapp/__init__.py0000664000175000017500000000154612203712502022510 0ustar takakitakakifrom pyramid.compat import escape from pyramid.security import view_execution_permitted from pyramid.response import Response def x_view(request): # pragma: no cover return Response('this is private!') def test(context, request): # should return false msg = 'Allow ./x? %s' % repr(view_execution_permitted( context, request, 'x')) return Response(escape(msg)) def includeme(config): from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config.add_view(test, name='test') config.add_view(x_view, name='x', permission='private') pyramid-1.4.5/pyramid/tests/pkgs/static_assetspec/0000775000175000017500000000000012210157153021573 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/static_assetspec/__init__.py0000664000175000017500000000012212203712502023674 0ustar takakitakakidef includeme(config): config.add_static_view('/', 'pyramid.tests:fixtures') pyramid-1.4.5/pyramid/tests/pkgs/includeapp1/0000775000175000017500000000000012210157153020437 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/includeapp1/three.py0000664000175000017500000000047212203712502022120 0ustar takakitakakifrom pyramid.response import Response def aview(request): return Response('three') def configure(config): config.add_view(aview, name='three') config.include('pyramid.tests.pkgs.includeapp1.two.configure') # should not cycle config.add_view(aview) # will be overridden by root when resolved pyramid-1.4.5/pyramid/tests/pkgs/includeapp1/two.py0000664000175000017500000000044012203712502021615 0ustar takakitakakifrom pyramid.response import Response def aview(request): return Response('two') def configure(config): config.add_view(aview, name='two') config.include('pyramid.tests.pkgs.includeapp1.three.configure') config.add_view(aview) # will be overridden by root when resolved pyramid-1.4.5/pyramid/tests/pkgs/includeapp1/__init__.py0000664000175000017500000000001612203712502022542 0ustar takakitakaki# include app pyramid-1.4.5/pyramid/tests/pkgs/includeapp1/root.py0000664000175000017500000000034612203712502021774 0ustar takakitakakifrom pyramid.response import Response def aview(request): return Response('root') def configure(config): config.add_view(aview) config.include('pyramid.tests.pkgs.includeapp1.two.configure') config.commit() pyramid-1.4.5/pyramid/tests/pkgs/subrequestapp/0000775000175000017500000000000012210157153021135 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/subrequestapp/__init__.py0000664000175000017500000000305212203712502023243 0ustar takakitakakifrom pyramid.config import Configurator from pyramid.request import Request def view_one(request): subreq = Request.blank('/view_two') response = request.invoke_subrequest(subreq, use_tweens=False) return response def view_two(request): return 'This came from view_two' def view_three(request): subreq = Request.blank('/view_four') try: return request.invoke_subrequest(subreq, use_tweens=True) except: # pragma: no cover request.response.body = b'Value error raised' return request.response def view_four(request): raise ValueError('foo') def view_five(request): subreq = Request.blank('/view_four') try: return request.invoke_subrequest(subreq, use_tweens=False) except ValueError: request.response.body = b'Value error raised' return request.response def excview(request): request.response.status_int = 500 request.response.body = b'Bad stuff happened' return request.response def main(): config = Configurator() config.add_route('one', '/view_one') config.add_route('two', '/view_two') config.add_route('three', '/view_three') config.add_route('four', '/view_four') config.add_route('five', '/view_five') config.add_view(excview, context=Exception) config.add_view(view_one, route_name='one') config.add_view(view_two, route_name='two', renderer='string') config.add_view(view_three, route_name='three') config.add_view(view_four, route_name='four') config.add_view(view_five, route_name='five') return config pyramid-1.4.5/pyramid/tests/pkgs/forbiddenview/0000775000175000017500000000000012210157153021061 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/forbiddenview/__init__.py0000664000175000017500000000202612203712502023167 0ustar takakitakakifrom pyramid.view import forbidden_view_config, view_config from pyramid.response import Response from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy @forbidden_view_config(route_name='foo') def foo_forbidden(request): # pragma: no cover return Response('foo_forbidden') @forbidden_view_config() def forbidden(request): return Response('generic_forbidden') @view_config(route_name='foo') def foo(request): # pragma: no cover return Response('OK foo') @view_config(route_name='bar') def bar(request): # pragma: no cover return Response('OK bar') def includeme(config): authn_policy = AuthTktAuthenticationPolicy('seekri1', hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config.set_default_permission('a') config.add_route('foo', '/foo') config.add_route('bar', '/bar') config.scan('pyramid.tests.pkgs.forbiddenview') pyramid-1.4.5/pyramid/tests/pkgs/forbiddenapp/0000775000175000017500000000000012210157153020667 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/forbiddenapp/__init__.py0000664000175000017500000000161312203712502022776 0ustar takakitakakifrom webob import Response from pyramid.httpexceptions import HTTPForbidden from pyramid.compat import bytes_ def x_view(request): # pragma: no cover return Response('this is private!') def forbidden_view(context, request): msg = context.message result = context.result message = msg + '\n' + str(result) resp = HTTPForbidden() resp.body = bytes_(message) return resp def includeme(config): from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy authn_policy = AuthTktAuthenticationPolicy('seekr1t', hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config._set_authentication_policy(authn_policy) config._set_authorization_policy(authz_policy) config.add_view(x_view, name='x', permission='private') config.add_view(forbidden_view, context=HTTPForbidden) pyramid-1.4.5/pyramid/tests/pkgs/ccbugapp/0000775000175000017500000000000012210157153020016 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/ccbugapp/__init__.py0000664000175000017500000000071312203712502022125 0ustar takakitakakifrom webob import Response def rdf_view(request): """ """ return Response('rdf') def juri_view(request): """ """ return Response('juri') def includeme(config): config.add_route('rdf', 'licenses/:license_code/:license_version/rdf') config.add_route('juri', 'licenses/:license_code/:license_version/:jurisdiction') config.add_view(rdf_view, route_name='rdf') config.add_view(juri_view, route_name='juri') pyramid-1.4.5/pyramid/tests/pkgs/notfoundview/0000775000175000017500000000000012210157153020761 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/notfoundview/__init__.py0000664000175000017500000000150612203712502023071 0ustar takakitakakifrom pyramid.view import notfound_view_config, view_config from pyramid.response import Response @notfound_view_config(route_name='foo', append_slash=True) def foo_notfound(request): # pragma: no cover return Response('foo_notfound') @notfound_view_config(route_name='baz') def baz_notfound(request): return Response('baz_notfound') @notfound_view_config(append_slash=True) def notfound(request): return Response('generic_notfound') @view_config(route_name='bar') def bar(request): return Response('OK bar') @view_config(route_name='foo2') def foo2(request): return Response('OK foo2') def includeme(config): config.add_route('foo', '/foo') config.add_route('foo2', '/foo/') config.add_route('bar', '/bar/') config.add_route('baz', '/baz') config.scan('pyramid.tests.pkgs.notfoundview') pyramid-1.4.5/pyramid/tests/pkgs/defpermbugapp/0000775000175000017500000000000012210157153021053 5ustar takakitakakipyramid-1.4.5/pyramid/tests/pkgs/defpermbugapp/__init__.py0000664000175000017500000000170312203712502023162 0ustar takakitakakifrom webob import Response from pyramid.security import NO_PERMISSION_REQUIRED from pyramid.view import view_config @view_config(name='x') def x_view(request): # pragma: no cover return Response('this is private!') @view_config(name='y', permission='private2') def y_view(request): # pragma: no cover return Response('this is private too!') @view_config(name='z', permission=NO_PERMISSION_REQUIRED) def z_view(request): return Response('this is public') def includeme(config): from pyramid.authorization import ACLAuthorizationPolicy from pyramid.authentication import AuthTktAuthenticationPolicy authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config.scan('pyramid.tests.pkgs.defpermbugapp') config._set_authentication_policy(authn_policy) config._set_authorization_policy(authz_policy) config.set_default_permission('private') pyramid-1.4.5/pyramid/tests/test_urldispatch.py0000664000175000017500000005635212210154276021237 0ustar takakitakakiimport unittest from pyramid import testing from pyramid.compat import ( text_, PY3, ) class TestRoute(unittest.TestCase): def _getTargetClass(self): from pyramid.urldispatch import Route return Route def _makeOne(self, *arg): return self._getTargetClass()(*arg) def test_provides_IRoute(self): from pyramid.interfaces import IRoute from zope.interface.verify import verifyObject verifyObject(IRoute, self._makeOne('name', 'pattern')) def test_ctor(self): import types route = self._makeOne('name', ':path', 'factory') self.assertEqual(route.pattern, ':path') self.assertEqual(route.path, ':path') self.assertEqual(route.name, 'name') self.assertEqual(route.factory, 'factory') self.assertTrue(route.generate.__class__ is types.FunctionType) self.assertTrue(route.match.__class__ is types.FunctionType) def test_ctor_defaults(self): import types route = self._makeOne('name', ':path') self.assertEqual(route.pattern, ':path') self.assertEqual(route.path, ':path') self.assertEqual(route.name, 'name') self.assertEqual(route.factory, None) self.assertTrue(route.generate.__class__ is types.FunctionType) self.assertTrue(route.match.__class__ is types.FunctionType) def test_match(self): route = self._makeOne('name', ':path') self.assertEqual(route.match('/whatever'), {'path':'whatever'}) def test_generate(self): route = self._makeOne('name', ':path') self.assertEqual(route.generate({'path':'abc'}), '/abc') class RoutesMapperTests(unittest.TestCase): def setUp(self): testing.setUp() def tearDown(self): testing.tearDown() def _getRequest(self, **kw): from pyramid.threadlocal import get_current_registry environ = {'SERVER_NAME':'localhost', 'wsgi.url_scheme':'http'} environ.update(kw) request = DummyRequest(environ) reg = get_current_registry() request.registry = reg return request def _getTargetClass(self): from pyramid.urldispatch import RoutesMapper return RoutesMapper def _makeOne(self): klass = self._getTargetClass() return klass() def test_provides_IRoutesMapper(self): from pyramid.interfaces import IRoutesMapper from zope.interface.verify import verifyObject verifyObject(IRoutesMapper, self._makeOne()) def test_no_route_matches(self): mapper = self._makeOne() request = self._getRequest(PATH_INFO='/') result = mapper(request) self.assertEqual(result['match'], None) self.assertEqual(result['route'], None) def test_connect_name_exists_removes_old(self): mapper = self._makeOne() mapper.connect('foo', 'archives/:action/:article') mapper.connect('foo', 'archives/:action/:article2') self.assertEqual(len(mapper.routelist), 1) self.assertEqual(len(mapper.routes), 1) self.assertEqual(mapper.routes['foo'].pattern, 'archives/:action/:article2') self.assertEqual(mapper.routelist[0].pattern, 'archives/:action/:article2') def test_connect_static(self): mapper = self._makeOne() mapper.connect('foo', 'archives/:action/:article', static=True) self.assertEqual(len(mapper.routelist), 0) self.assertEqual(len(mapper.routes), 1) self.assertEqual(mapper.routes['foo'].pattern, 'archives/:action/:article') def test_connect_static_overridden(self): mapper = self._makeOne() mapper.connect('foo', 'archives/:action/:article', static=True) self.assertEqual(len(mapper.routelist), 0) self.assertEqual(len(mapper.routes), 1) self.assertEqual(mapper.routes['foo'].pattern, 'archives/:action/:article') mapper.connect('foo', 'archives/:action/:article2') self.assertEqual(len(mapper.routelist), 1) self.assertEqual(len(mapper.routes), 1) self.assertEqual(mapper.routes['foo'].pattern, 'archives/:action/:article2') self.assertEqual(mapper.routelist[0].pattern, 'archives/:action/:article2') def test___call__pathinfo_cant_be_decoded(self): from pyramid.exceptions import URLDecodeError mapper = self._makeOne() if PY3: # pragma: no cover path_info = b'\xff\xfe\xe6\x00'.decode('latin-1') else: path_info = b'\xff\xfe\xe6\x00' request = self._getRequest(PATH_INFO=path_info) self.assertRaises(URLDecodeError, mapper, request) def test___call__route_matches(self): mapper = self._makeOne() mapper.connect('foo', 'archives/:action/:article') request = self._getRequest(PATH_INFO='/archives/action1/article1') result = mapper(request) self.assertEqual(result['route'], mapper.routes['foo']) self.assertEqual(result['match']['action'], 'action1') self.assertEqual(result['match']['article'], 'article1') def test___call__route_matches_with_predicates(self): mapper = self._makeOne() mapper.connect('foo', 'archives/:action/:article', predicates=[lambda *arg: True]) request = self._getRequest(PATH_INFO='/archives/action1/article1') result = mapper(request) self.assertEqual(result['route'], mapper.routes['foo']) self.assertEqual(result['match']['action'], 'action1') self.assertEqual(result['match']['article'], 'article1') def test___call__route_fails_to_match_with_predicates(self): mapper = self._makeOne() mapper.connect('foo', 'archives/:action/article1', predicates=[lambda *arg: True, lambda *arg: False]) mapper.connect('bar', 'archives/:action/:article') request = self._getRequest(PATH_INFO='/archives/action1/article1') result = mapper(request) self.assertEqual(result['route'], mapper.routes['bar']) self.assertEqual(result['match']['action'], 'action1') self.assertEqual(result['match']['article'], 'article1') def test___call__custom_predicate_gets_info(self): mapper = self._makeOne() def pred(info, request): self.assertEqual(info['match'], {'action':'action1'}) self.assertEqual(info['route'], mapper.routes['foo']) return True mapper.connect('foo', 'archives/:action/article1', predicates=[pred]) request = self._getRequest(PATH_INFO='/archives/action1/article1') mapper(request) def test_cc_bug(self): # "unordered" as reported in IRC by author of # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ mapper = self._makeOne() mapper.connect('rdf', 'licenses/:license_code/:license_version/rdf') mapper.connect('juri', 'licenses/:license_code/:license_version/:jurisdiction') request = self._getRequest(PATH_INFO='/licenses/1/v2/rdf') result = mapper(request) self.assertEqual(result['route'], mapper.routes['rdf']) self.assertEqual(result['match']['license_code'], '1') self.assertEqual(result['match']['license_version'], 'v2') request = self._getRequest(PATH_INFO='/licenses/1/v2/usa') result = mapper(request) self.assertEqual(result['route'], mapper.routes['juri']) self.assertEqual(result['match']['license_code'], '1') self.assertEqual(result['match']['license_version'], 'v2') self.assertEqual(result['match']['jurisdiction'], 'usa') def test___call__root_route_matches(self): mapper = self._makeOne() mapper.connect('root', '') request = self._getRequest(PATH_INFO='/') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) self.assertEqual(result['match'], {}) def test___call__root_route_matches2(self): mapper = self._makeOne() mapper.connect('root', '/') request = self._getRequest(PATH_INFO='/') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) self.assertEqual(result['match'], {}) def test___call__root_route_when_path_info_empty(self): mapper = self._makeOne() mapper.connect('root', '/') request = self._getRequest(PATH_INFO='') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) self.assertEqual(result['match'], {}) def test___call__root_route_when_path_info_notempty(self): mapper = self._makeOne() mapper.connect('root', '/') request = self._getRequest(PATH_INFO='/') result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) self.assertEqual(result['match'], {}) def test___call__no_path_info(self): mapper = self._makeOne() mapper.connect('root', '/') request = self._getRequest() result = mapper(request) self.assertEqual(result['route'], mapper.routes['root']) self.assertEqual(result['match'], {}) def test_has_routes(self): mapper = self._makeOne() self.assertEqual(mapper.has_routes(), False) mapper.connect('whatever', 'archives/:action/:article') self.assertEqual(mapper.has_routes(), True) def test_get_routes(self): from pyramid.urldispatch import Route mapper = self._makeOne() self.assertEqual(mapper.get_routes(), []) mapper.connect('whatever', 'archives/:action/:article') routes = mapper.get_routes() self.assertEqual(len(routes), 1) self.assertEqual(routes[0].__class__, Route) def test_get_route_matches(self): mapper = self._makeOne() mapper.connect('whatever', 'archives/:action/:article') result = mapper.get_route('whatever') self.assertEqual(result.pattern, 'archives/:action/:article') def test_get_route_misses(self): mapper = self._makeOne() result = mapper.get_route('whatever') self.assertEqual(result, None) def test_generate(self): mapper = self._makeOne() def generator(kw): return 123 route = DummyRoute(generator) mapper.routes['abc'] = route self.assertEqual(mapper.generate('abc', {}), 123) class TestCompileRoute(unittest.TestCase): def _callFUT(self, pattern): from pyramid.urldispatch import _compile_route return _compile_route(pattern) def test_no_star(self): matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar') self.assertEqual(matcher('/foo/baz/biz/buz/bar'), {'baz':'baz', 'buz':'buz'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator({'baz':1, 'buz':2}), '/foo/1/biz/2/bar') def test_with_star(self): matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar*traverse') self.assertEqual(matcher('/foo/baz/biz/buz/bar'), {'baz':'baz', 'buz':'buz', 'traverse':()}) self.assertEqual(matcher('/foo/baz/biz/buz/bar/everything/else/here'), {'baz':'baz', 'buz':'buz', 'traverse':('everything', 'else', 'here')}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator( {'baz':1, 'buz':2, 'traverse':'/a/b'}), '/foo/1/biz/2/bar/a/b') def test_with_bracket_star(self): matcher, generator = self._callFUT( '/foo/{baz}/biz/{buz}/bar{remainder:.*}') self.assertEqual(matcher('/foo/baz/biz/buz/bar'), {'baz':'baz', 'buz':'buz', 'remainder':''}) self.assertEqual(matcher('/foo/baz/biz/buz/bar/everything/else/here'), {'baz':'baz', 'buz':'buz', 'remainder':'/everything/else/here'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator( {'baz':1, 'buz':2, 'remainder':'/a/b'}), '/foo/1/biz/2/bar%2Fa%2Fb') def test_no_beginning_slash(self): matcher, generator = self._callFUT('foo/:baz/biz/:buz/bar') self.assertEqual(matcher('/foo/baz/biz/buz/bar'), {'baz':'baz', 'buz':'buz'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator({'baz':1, 'buz':2}), '/foo/1/biz/2/bar') def test_custom_regex(self): matcher, generator = self._callFUT('foo/{baz}/biz/{buz:[^/\.]+}.{bar}') self.assertEqual(matcher('/foo/baz/biz/buz.bar'), {'baz':'baz', 'buz':'buz', 'bar':'bar'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html') def test_custom_regex_with_colons(self): matcher, generator = self._callFUT('foo/{baz}/biz/{buz:(?:[^/\.]+)}.{bar}') self.assertEqual(matcher('/foo/baz/biz/buz.bar'), {'baz':'baz', 'buz':'buz', 'bar':'bar'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html') def test_mixed_newstyle_oldstyle_pattern_defaults_to_newstyle(self): # pattern: '\\/foo\\/(?Pabc)\\/biz\\/(?P[^/]+)\\/bar$' # note presence of :abc in pattern (oldstyle match) matcher, generator = self._callFUT('foo/{baz:abc}/biz/{buz}/bar') self.assertEqual(matcher('/foo/abc/biz/buz/bar'), {'baz':'abc', 'buz':'buz'}) self.assertEqual(generator({'baz':1, 'buz':2}), '/foo/1/biz/2/bar') def test_custom_regex_with_embedded_squigglies(self): matcher, generator = self._callFUT('/{buz:\d{4}}') self.assertEqual(matcher('/2001'), {'buz':'2001'}) self.assertEqual(matcher('/200'), None) self.assertEqual(generator({'buz':2001}), '/2001') def test_custom_regex_with_embedded_squigglies2(self): matcher, generator = self._callFUT('/{buz:\d{3,4}}') self.assertEqual(matcher('/2001'), {'buz':'2001'}) self.assertEqual(matcher('/200'), {'buz':'200'}) self.assertEqual(matcher('/20'), None) self.assertEqual(generator({'buz':2001}), '/2001') def test_custom_regex_with_embedded_squigglies3(self): matcher, generator = self._callFUT( '/{buz:(\d{2}|\d{4})-[a-zA-Z]{3,4}-\d{2}}') self.assertEqual(matcher('/2001-Nov-15'), {'buz':'2001-Nov-15'}) self.assertEqual(matcher('/99-June-10'), {'buz':'99-June-10'}) self.assertEqual(matcher('/2-Nov-15'), None) self.assertEqual(matcher('/200-Nov-15'), None) self.assertEqual(matcher('/2001-No-15'), None) self.assertEqual(generator({'buz':'2001-Nov-15'}), '/2001-Nov-15') self.assertEqual(generator({'buz':'99-June-10'}), '/99-June-10') def test_pattern_with_high_order_literal(self): pattern = text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8') matcher, generator = self._callFUT(pattern) self.assertEqual(matcher(text_(b'/La Pe\xc3\xb1a/x', 'utf-8')), {'x':'x'}) self.assertEqual(generator({'x':'1'}), '/La%20Pe%C3%B1a/1') def test_pattern_generate_with_high_order_dynamic(self): pattern = '/{x}' _, generator = self._callFUT(pattern) self.assertEqual( generator({'x':text_(b'La Pe\xc3\xb1a', 'utf-8')}), '/La%20Pe%C3%B1a') def test_docs_sample_generate(self): # sample from urldispatch.rst pattern = text_(b'/La Pe\xc3\xb1a/{city}', 'utf-8') _, generator = self._callFUT(pattern) self.assertEqual( generator({'city':text_(b'Qu\xc3\xa9bec', 'utf-8')}), '/La%20Pe%C3%B1a/Qu%C3%A9bec') def test_generate_with_mixedtype_values(self): pattern = '/{city}/{state}' _, generator = self._callFUT(pattern) result = generator( {'city': text_(b'Qu\xc3\xa9bec', 'utf-8'), 'state': b'La Pe\xc3\xb1a'} ) self.assertEqual(result, '/Qu%C3%A9bec/La%20Pe%C3%B1a') # should be a native string self.assertEqual(type(result), str) def test_highorder_pattern_utf8(self): pattern = b'/La Pe\xc3\xb1a/{city}' self.assertRaises(ValueError, self._callFUT, pattern) def test_generate_with_string_remainder_and_unicode_replacement(self): pattern = text_(b'/abc*remainder', 'utf-8') _, generator = self._callFUT(pattern) result = generator( {'remainder': text_(b'/Qu\xc3\xa9bec/La Pe\xc3\xb1a', 'utf-8')} ) self.assertEqual(result, '/abc/Qu%C3%A9bec/La%20Pe%C3%B1a') # should be a native string self.assertEqual(type(result), str) def test_generate_with_string_remainder_and_nonstring_replacement(self): pattern = text_(b'/abc/*remainder', 'utf-8') _, generator = self._callFUT(pattern) result = generator( {'remainder': None} ) self.assertEqual(result, '/abc/None') # should be a native string self.assertEqual(type(result), str) class TestCompileRouteFunctional(unittest.TestCase): def matches(self, pattern, path, expected): from pyramid.urldispatch import _compile_route matcher = _compile_route(pattern)[0] result = matcher(path) self.assertEqual(result, expected) def generates(self, pattern, dict, result): from pyramid.urldispatch import _compile_route self.assertEqual(_compile_route(pattern)[1](dict), result) def test_matcher_functional_notdynamic(self): self.matches('/', '', None) self.matches('', '', None) self.matches('/', '/foo', None) self.matches('/foo/', '/foo', None) self.matches('', '/', {}) self.matches('/', '/', {}) def test_matcher_functional_newstyle(self): self.matches('/{x}', '', None) self.matches('/{x}', '/', None) self.matches('/abc/{def}', '/abc/', None) self.matches('/{x}', '/a', {'x':'a'}) self.matches('zzz/{x}', '/zzz/abc', {'x':'abc'}) self.matches('zzz/{x}*traverse', '/zzz/abc', {'x':'abc', 'traverse':()}) self.matches('zzz/{x}*traverse', '/zzz/abc/def/g', {'x':'abc', 'traverse':('def', 'g')}) self.matches('*traverse', '/zzz/abc', {'traverse':('zzz', 'abc')}) self.matches('*traverse', '/zzz/ abc', {'traverse':('zzz', ' abc')}) #'/La%20Pe%C3%B1a' self.matches('{x}', text_(b'/La Pe\xc3\xb1a', 'utf-8'), {'x':text_(b'La Pe\xc3\xb1a', 'utf-8')}) # '/La%20Pe%C3%B1a/x' self.matches('*traverse', text_(b'/La Pe\xc3\xb1a/x'), {'traverse':(text_(b'La Pe\xc3\xb1a'), 'x')}) self.matches('/foo/{id}.html', '/foo/bar.html', {'id':'bar'}) self.matches('/{num:[0-9]+}/*traverse', '/555/abc/def', {'num':'555', 'traverse':('abc', 'def')}) self.matches('/{num:[0-9]*}/*traverse', '/555/abc/def', {'num':'555', 'traverse':('abc', 'def')}) self.matches('zzz/{_}', '/zzz/abc', {'_':'abc'}) self.matches('zzz/{_abc}', '/zzz/abc', {'_abc':'abc'}) self.matches('zzz/{abc_def}', '/zzz/abc', {'abc_def':'abc'}) def test_matcher_functional_oldstyle(self): self.matches('/:x', '', None) self.matches('/:x', '/', None) self.matches('/abc/:def', '/abc/', None) self.matches('/:x', '/a', {'x':'a'}) self.matches('zzz/:x', '/zzz/abc', {'x':'abc'}) self.matches('zzz/:x*traverse', '/zzz/abc', {'x':'abc', 'traverse':()}) self.matches('zzz/:x*traverse', '/zzz/abc/def/g', {'x':'abc', 'traverse':('def', 'g')}) self.matches('*traverse', '/zzz/abc', {'traverse':('zzz', 'abc')}) self.matches('*traverse', '/zzz/ abc', {'traverse':('zzz', ' abc')}) #'/La%20Pe%C3%B1a' # pattern, path, expected self.matches(':x', text_(b'/La Pe\xc3\xb1a', 'utf-8'), {'x':text_(b'La Pe\xc3\xb1a', 'utf-8')}) # '/La%20Pe%C3%B1a/x' self.matches('*traverse', text_(b'/La Pe\xc3\xb1a/x', 'utf-8'), {'traverse':(text_(b'La Pe\xc3\xb1a', 'utf-8'), 'x')}) self.matches('/foo/:id.html', '/foo/bar.html', {'id':'bar'}) self.matches('/foo/:id_html', '/foo/bar_html', {'id_html':'bar_html'}) self.matches('zzz/:_', '/zzz/abc', {'_':'abc'}) self.matches('zzz/:_abc', '/zzz/abc', {'_abc':'abc'}) self.matches('zzz/:abc_def', '/zzz/abc', {'abc_def':'abc'}) def test_generator_functional_notdynamic(self): self.generates('', {}, '/') self.generates('/', {}, '/') def test_generator_functional_newstyle(self): self.generates('/{x}', {'x':''}, '/') self.generates('/{x}', {'x':'a'}, '/a') self.generates('zzz/{x}', {'x':'abc'}, '/zzz/abc') self.generates('zzz/{x}*traverse', {'x':'abc', 'traverse':''}, '/zzz/abc') self.generates('zzz/{x}*traverse', {'x':'abc', 'traverse':'/def/g'}, '/zzz/abc/def/g') self.generates('/{x}', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')}, '/%2FLa%20Pe%C3%B1a') self.generates('/{x}*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'), 'y':'/rest/of/path'}, '/%2FLa%20Pe%C3%B1a/rest/of/path') self.generates('*traverse', {'traverse':('a', text_(b'La Pe\xf1a'))}, '/a/La%20Pe%C3%B1a') self.generates('/foo/{id}.html', {'id':'bar'}, '/foo/bar.html') self.generates('/foo/{_}', {'_':'20'}, '/foo/20') self.generates('/foo/{_abc}', {'_abc':'20'}, '/foo/20') self.generates('/foo/{abc_def}', {'abc_def':'20'}, '/foo/20') def test_generator_functional_oldstyle(self): self.generates('/:x', {'x':''}, '/') self.generates('/:x', {'x':'a'}, '/a') self.generates('zzz/:x', {'x':'abc'}, '/zzz/abc') self.generates('zzz/:x*traverse', {'x':'abc', 'traverse':''}, '/zzz/abc') self.generates('zzz/:x*traverse', {'x':'abc', 'traverse':'/def/g'}, '/zzz/abc/def/g') self.generates('/:x', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')}, '/%2FLa%20Pe%C3%B1a') self.generates('/:x*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'), 'y':'/rest/of/path'}, '/%2FLa%20Pe%C3%B1a/rest/of/path') self.generates('*traverse', {'traverse':('a', text_(b'La Pe\xf1a'))}, '/a/La%20Pe%C3%B1a') self.generates('/foo/:id.html', {'id':'bar'}, '/foo/bar.html') self.generates('/foo/:_', {'_':'20'}, '/foo/20') self.generates('/foo/:_abc', {'_abc':'20'}, '/foo/20') self.generates('/foo/:abc_def', {'abc_def':'20'}, '/foo/20') class DummyContext(object): """ """ class DummyRequest(object): def __init__(self, environ): self.environ = environ class DummyRoute(object): def __init__(self, generator): self.generate = generator pyramid-1.4.5/pyramid/tests/test_security.py0000664000175000017500000003540112203712502020546 0ustar takakitakakiimport unittest from pyramid.testing import cleanUp class TestAllPermissionsList(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _getTargetClass(self): from pyramid.security import AllPermissionsList return AllPermissionsList def _makeOne(self): return self._getTargetClass()() def test_it(self): thing = self._makeOne() self.assertTrue(thing.__eq__(thing)) self.assertEqual(thing.__iter__(), ()) self.assertTrue('anything' in thing) def test_singleton(self): from pyramid.security import ALL_PERMISSIONS self.assertEqual(ALL_PERMISSIONS.__class__, self._getTargetClass()) class TestAllowed(unittest.TestCase): def _getTargetClass(self): from pyramid.security import Allowed return Allowed def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_it(self): allowed = self._makeOne('hello') self.assertEqual(allowed.msg, 'hello') self.assertEqual(allowed, True) self.assertTrue(allowed) self.assertEqual(str(allowed), 'hello') self.assertTrue('" in repr(allowed)) class TestDenied(unittest.TestCase): def _getTargetClass(self): from pyramid.security import Denied return Denied def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_it(self): denied = self._makeOne('hello') self.assertEqual(denied.msg, 'hello') self.assertEqual(denied, False) self.assertFalse(denied) self.assertEqual(str(denied), 'hello') self.assertTrue('" in repr(denied)) class TestACLAllowed(unittest.TestCase): def _getTargetClass(self): from pyramid.security import ACLAllowed return ACLAllowed def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_it(self): msg = ("ACLAllowed permission 'permission' via ACE 'ace' in ACL 'acl' " "on context 'ctx' for principals 'principals'") allowed = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') self.assertTrue(msg in allowed.msg) self.assertEqual(allowed, True) self.assertTrue(allowed) self.assertEqual(str(allowed), msg) self.assertTrue('" % msg in repr(allowed)) class TestACLDenied(unittest.TestCase): def _getTargetClass(self): from pyramid.security import ACLDenied return ACLDenied def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_it(self): msg = ("ACLDenied permission 'permission' via ACE 'ace' in ACL 'acl' " "on context 'ctx' for principals 'principals'") denied = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') self.assertTrue(msg in denied.msg) self.assertEqual(denied, False) self.assertFalse(denied) self.assertEqual(str(denied), msg) self.assertTrue('" % msg in repr(denied)) class TestViewExecutionPermitted(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, *arg, **kw): from pyramid.security import view_execution_permitted return view_execution_permitted(*arg, **kw) def _registerSecuredView(self, view_name, allow=True): from pyramid.threadlocal import get_current_registry from zope.interface import Interface from pyramid.interfaces import ISecuredView from pyramid.interfaces import IViewClassifier class Checker(object): def __permitted__(self, context, request): self.context = context self.request = request return allow checker = Checker() reg = get_current_registry() reg.registerAdapter(checker, (IViewClassifier, Interface, Interface), ISecuredView, view_name) return checker def test_no_permission(self): from zope.interface import Interface from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ISettings from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier settings = dict(debug_authorization=True) reg = get_current_registry() reg.registerUtility(settings, ISettings) context = DummyContext() request = DummyRequest({}) class DummyView(object): pass view = DummyView() reg.registerAdapter(view, (IViewClassifier, Interface, Interface), IView, '') result = self._callFUT(context, request, '') msg = result.msg self.assertTrue("Allowed: view name '' in context" in msg) self.assertTrue('(no permission defined)' in msg) self.assertEqual(result, True) def test_no_view_registered(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ISettings settings = dict(debug_authorization=True) reg = get_current_registry() reg.registerUtility(settings, ISettings) context = DummyContext() request = DummyRequest({}) self.assertRaises(TypeError, self._callFUT, context, request, '') def test_with_permission(self): from zope.interface import Interface from zope.interface import directlyProvides from pyramid.interfaces import IRequest class IContext(Interface): pass context = DummyContext() directlyProvides(context, IContext) self._registerSecuredView('', True) request = DummyRequest({}) directlyProvides(request, IRequest) result = self._callFUT(context, request, '') self.assertTrue(result is True) class TestHasPermission(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, *arg): from pyramid.security import has_permission return has_permission(*arg) def test_no_authentication_policy(self): request = _makeRequest() result = self._callFUT('view', None, request) self.assertEqual(result, True) self.assertEqual(result.msg, 'No authentication policy in use.') def test_authentication_policy_no_authorization_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, None) self.assertRaises(ValueError, self._callFUT, 'view', None, request) def test_authn_and_authz_policies_registered(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, None) _registerAuthorizationPolicy(request.registry, 'yo') self.assertEqual(self._callFUT('view', None, request), 'yo') def test_no_registry_on_request(self): from pyramid.threadlocal import get_current_registry request = DummyRequest({}) registry = get_current_registry() _registerAuthenticationPolicy(registry, None) _registerAuthorizationPolicy(registry, 'yo') self.assertEqual(self._callFUT('view', None, request), 'yo') class TestAuthenticatedUserId(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.security import authenticated_userid return authenticated_userid(request) def test_no_authentication_policy(self): request = _makeRequest() result = self._callFUT(request) self.assertEqual(result, None) def test_with_authentication_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') def test_with_authentication_policy_no_reg_on_request(self): from pyramid.threadlocal import get_current_registry request = DummyRequest({}) registry = get_current_registry() _registerAuthenticationPolicy(registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') class TestUnauthenticatedUserId(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.security import unauthenticated_userid return unauthenticated_userid(request) def test_no_authentication_policy(self): request = _makeRequest() result = self._callFUT(request) self.assertEqual(result, None) def test_with_authentication_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') def test_with_authentication_policy_no_reg_on_request(self): from pyramid.threadlocal import get_current_registry request = DummyRequest({}) registry = get_current_registry() _registerAuthenticationPolicy(registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') class TestEffectivePrincipals(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.security import effective_principals return effective_principals(request) def test_no_authentication_policy(self): from pyramid.security import Everyone request = _makeRequest() result = self._callFUT(request) self.assertEqual(result, [Everyone]) def test_with_authentication_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') def test_with_authentication_policy_no_reg_on_request(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() request = DummyRequest({}) _registerAuthenticationPolicy(registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') class TestPrincipalsAllowedByPermission(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, *arg): from pyramid.security import principals_allowed_by_permission return principals_allowed_by_permission(*arg) def test_no_authorization_policy(self): from pyramid.security import Everyone context = DummyContext() result = self._callFUT(context, 'view') self.assertEqual(result, [Everyone]) def test_with_authorization_policy(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() _registerAuthorizationPolicy(registry, 'yo') context = DummyContext() result = self._callFUT(context, 'view') self.assertEqual(result, 'yo') class TestRemember(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, *arg): from pyramid.security import remember return remember(*arg) def test_no_authentication_policy(self): request = _makeRequest() result = self._callFUT(request, 'me') self.assertEqual(result, []) def test_with_authentication_policy(self): request = _makeRequest() registry = request.registry _registerAuthenticationPolicy(registry, 'yo') result = self._callFUT(request, 'me') self.assertEqual(result, 'yo') def test_with_authentication_policy_no_reg_on_request(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() request = DummyRequest({}) _registerAuthenticationPolicy(registry, 'yo') result = self._callFUT(request, 'me') self.assertEqual(result, 'yo') class TestForget(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, *arg): from pyramid.security import forget return forget(*arg) def test_no_authentication_policy(self): request = _makeRequest() result = self._callFUT(request) self.assertEqual(result, []) def test_with_authentication_policy(self): request = _makeRequest() _registerAuthenticationPolicy(request.registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') def test_with_authentication_policy_no_reg_on_request(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() request = DummyRequest({}) _registerAuthenticationPolicy(registry, 'yo') result = self._callFUT(request) self.assertEqual(result, 'yo') class DummyContext: def __init__(self, *arg, **kw): self.__dict__.update(kw) class DummyRequest: def __init__(self, environ): self.environ = environ class DummyAuthenticationPolicy: def __init__(self, result): self.result = result def effective_principals(self, request): return self.result def unauthenticated_userid(self, request): return self.result def authenticated_userid(self, request): return self.result def remember(self, request, principal, **kw): return self.result def forget(self, request): return self.result class DummyAuthorizationPolicy: def __init__(self, result): self.result = result def permits(self, context, principals, permission): return self.result def principals_allowed_by_permission(self, context, permission): return self.result def _registerAuthenticationPolicy(reg, result): from pyramid.interfaces import IAuthenticationPolicy policy = DummyAuthenticationPolicy(result) reg.registerUtility(policy, IAuthenticationPolicy) return policy def _registerAuthorizationPolicy(reg, result): from pyramid.interfaces import IAuthorizationPolicy policy = DummyAuthorizationPolicy(result) reg.registerUtility(policy, IAuthorizationPolicy) return policy def _makeRequest(): from pyramid.registry import Registry request = DummyRequest({}) request.registry = Registry() return request pyramid-1.4.5/pyramid/tests/test_renderers.py0000664000175000017500000011050212203712502020664 0ustar takakitakakiimport unittest from pyramid.testing import cleanUp from pyramid import testing from pyramid.compat import text_ class TestTemplateRendererFactory(unittest.TestCase): def setUp(self): self.config = cleanUp() def tearDown(self): cleanUp() def _callFUT(self, info, impl): from pyramid.renderers import template_renderer_factory return template_renderer_factory(info, impl) def test_lookup_found(self): from pyramid.interfaces import IChameleonLookup L = [] def dummy(info): L.append(info) return True self.config.registry.registerUtility(dummy, IChameleonLookup, name='abc') class DummyInfo(object): pass info = DummyInfo() info.registry = self.config.registry info.type = 'abc' result = self._callFUT(info, None) self.assertEqual(result, True) self.assertEqual(L, [info]) def test_lookup_miss(self): from pyramid.interfaces import ITemplateRenderer import os abspath = os.path.abspath(__file__) renderer = {} self.config.registry.registerUtility( renderer, ITemplateRenderer, name=abspath) info = DummyRendererInfo({ 'name':abspath, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) result = self._callFUT(info, None) self.assertTrue(result is renderer) class TestChameleonRendererLookup(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _makeOne(self, impl): from pyramid.renderers import ChameleonRendererLookup return ChameleonRendererLookup(impl, self.config.registry) def _registerTemplateRenderer(self, renderer, name): from pyramid.interfaces import ITemplateRenderer self.config.registry.registerUtility( renderer, ITemplateRenderer, name=name) def test_get_spec_not_abspath_no_colon_no_package(self): lookup = self._makeOne(None) result = lookup.get_spec('foo', None) self.assertEqual(result, 'foo') def test_get_spec_not_abspath_no_colon_with_package(self): from pyramid import tests lookup = self._makeOne(None) result = lookup.get_spec('foo', tests) self.assertEqual(result, 'pyramid.tests:foo') def test_get_spec_not_abspath_with_colon_no_package(self): lookup = self._makeOne(None) result = lookup.get_spec('fudge:foo', None) self.assertEqual(result, 'fudge:foo') def test_get_spec_not_abspath_with_colon_with_package(self): from pyramid import tests lookup = self._makeOne(None) result = lookup.get_spec('fudge:foo', tests) self.assertEqual(result, 'fudge:foo') def test_get_spec_is_abspath_no_colon_no_package(self): import os lookup = self._makeOne(None) spec = os.path.abspath(__file__) result = lookup.get_spec(spec, None) self.assertEqual(result, spec) def test_get_spec_is_abspath_no_colon_with_path_in_package(self): from pyramid import tests import os lookup = self._makeOne(None) f = __file__ spec = os.path.abspath(f) result = lookup.get_spec(spec, tests) self.assertEqual(result, 'pyramid.tests:%s' % os.path.split(f)[-1]) def test_get_spec_is_abspath_no_colon_with_path_outside_package(self): import venusian # used only because it's outside of pyramid.tests import os lookup = self._makeOne(None) f = __file__ spec = os.path.abspath(f) result = lookup.get_spec(spec, venusian) self.assertEqual(result, spec) def test_get_spec_is_abspath_with_colon_no_package(self): import os lookup = self._makeOne(None) spec = os.path.join(os.path.abspath(__file__), ':foo') result = lookup.get_spec(spec, None) self.assertEqual(result, spec) def test_get_spec_is_abspath_with_colon_with_path_in_package(self): from pyramid import tests import os lookup = self._makeOne(None) f = os.path.abspath(__file__) spec = os.path.join(f, ':foo') result = lookup.get_spec(spec, tests) tail = spec.split(os.sep)[-2:] self.assertEqual(result, 'pyramid.tests:%s/%s' % tuple(tail)) def test_get_spec_is_abspath_with_colon_with_path_outside_package(self): import venusian # used only because it's outside of pyramid.tests import os lookup = self._makeOne(None) spec = os.path.join(os.path.abspath(__file__), ':foo') result = lookup.get_spec(spec, venusian) self.assertEqual(result, spec) def test_translate(self): from pyramid.interfaces import IChameleonTranslate def t(): pass self.config.registry.registerUtility(t, IChameleonTranslate) lookup = self._makeOne(None) self.assertEqual(lookup.translate, t) def test_debug_settings_None(self): self.config.registry.settings = None lookup = self._makeOne(None) self.assertEqual(lookup.debug, False) def test_debug_settings_not_None(self): self.config.registry.settings = {'debug_templates':True} lookup = self._makeOne(None) self.assertEqual(lookup.debug, True) def test_auto_reload_settings_None(self): self.config.registry.settings = None lookup = self._makeOne(None) self.assertEqual(lookup.auto_reload, False) def test_auto_reload_settings_not_None(self): self.config.registry.settings = {'reload_templates':True} lookup = self._makeOne(None) self.assertEqual(lookup.auto_reload, True) def test___call__abspath_path_notexists(self): abspath = '/wont/exist' self._registerTemplateRenderer({}, abspath) info = DummyRendererInfo({ 'name':abspath, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(None) self.assertRaises(ValueError, lookup.__call__, info) def test___call__abspath_alreadyregistered(self): import os abspath = os.path.abspath(__file__) renderer = {} self._registerTemplateRenderer(renderer, abspath) info = DummyRendererInfo({ 'name':abspath, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(None) result = lookup(info) self.assertTrue(result is renderer) def test___call__abspath_notyetregistered(self): import os abspath = os.path.abspath(__file__) renderer = {} factory = DummyFactory(renderer) info = DummyRendererInfo({ 'name':abspath, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(factory) result = lookup(info) self.assertEqual(result, renderer) def test___call__relpath_path_registered(self): renderer = {} spec = 'foo/bar' self._registerTemplateRenderer(renderer, spec) info = DummyRendererInfo({ 'name':spec, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(None) result = lookup(info) self.assertTrue(renderer is result) def test___call__relpath_has_package_registered(self): renderer = {} import pyramid.tests spec = 'bar/baz' self._registerTemplateRenderer(renderer, 'pyramid.tests:bar/baz') info = DummyRendererInfo({ 'name':spec, 'package':pyramid.tests, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(None) result = lookup(info) self.assertTrue(renderer is result) def test___call__spec_notfound(self): spec = 'pyramid.tests:wont/exist' info = DummyRendererInfo({ 'name':spec, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(None) self.assertRaises(ValueError, lookup.__call__, info) def test___call__spec_alreadyregistered(self): from pyramid import tests module_name = tests.__name__ relpath = 'test_renderers.py' spec = '%s:%s' % (module_name, relpath) info = DummyRendererInfo({ 'name':spec, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) renderer = {} self._registerTemplateRenderer(renderer, spec) lookup = self._makeOne(None) result = lookup(info) self.assertTrue(result is renderer) def test___call__spec_notyetregistered(self): import os from pyramid import tests module_name = tests.__name__ relpath = 'test_renderers.py' renderer = {} factory = DummyFactory(renderer) spec = '%s:%s' % (module_name, relpath) info = DummyRendererInfo({ 'name':spec, 'package':None, 'registry':self.config.registry, 'settings':{}, 'type':'type', }) lookup = self._makeOne(factory) result = lookup(info) self.assertTrue(result is renderer) path = os.path.abspath(__file__).split('$')[0] # jython if path.endswith('.pyc'): # pragma: no cover path = path[:-1] self.assertTrue(factory.path.startswith(path)) self.assertEqual(factory.kw, {'macro':None}) def test___call__spec_withmacro(self): from pyramid.interfaces import ITemplateRenderer import os from pyramid import tests module_name = tests.__name__ relpath = 'fixtures/withmacro#foo.pt' renderer = {} factory = DummyFactory(renderer) spec = '%s:%s' % (module_name, relpath) reg = self.config.registry info = DummyRendererInfo({ 'name':spec, 'package':None, 'registry':reg, 'settings':{}, 'type':'type', }) lookup = self._makeOne(factory) result = lookup(info) self.assertTrue(result is renderer) path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'fixtures', 'withmacro.pt') self.assertTrue(factory.path.startswith(path)) self.assertEqual(factory.kw, {'macro':'foo'}) self.assertTrue( reg.getUtility(ITemplateRenderer, name=spec) is renderer ) def test___call__reload_assets_true(self): import pyramid.tests from pyramid.interfaces import ISettings from pyramid.interfaces import ITemplateRenderer settings = {'reload_assets':True} self.config.registry.registerUtility(settings, ISettings) renderer = {} factory = DummyFactory(renderer) spec = 'test_renderers.py' reg = self.config.registry info = DummyRendererInfo({ 'name':spec, 'package':pyramid.tests, 'registry':reg, 'settings':settings, 'type':'type', }) lookup = self._makeOne(factory) result = lookup(info) self.assertTrue(result is renderer) spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py') self.assertEqual(reg.queryUtility(ITemplateRenderer, name=spec), None) def test___call__reload_assets_false(self): import pyramid.tests from pyramid.interfaces import ITemplateRenderer settings = {'reload_assets':False} renderer = {} factory = DummyFactory(renderer) spec = 'test_renderers.py' reg = self.config.registry info = DummyRendererInfo({ 'name':spec, 'package':pyramid.tests, 'registry':reg, 'settings':settings, 'type':'type', }) lookup = self._makeOne(factory) result = lookup(info) self.assertTrue(result is renderer) spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py') self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec), None) class TestJSON(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _makeOne(self, **kw): from pyramid.renderers import JSON return JSON(**kw) def test_it(self): renderer = self._makeOne()(None) result = renderer({'a':1}, {}) self.assertEqual(result, '{"a": 1}') def test_with_request_content_type_notset(self): request = testing.DummyRequest() renderer = self._makeOne()(None) renderer({'a':1}, {'request':request}) self.assertEqual(request.response.content_type, 'application/json') def test_with_request_content_type_set(self): request = testing.DummyRequest() request.response.content_type = 'text/mishmash' renderer = self._makeOne()(None) renderer({'a':1}, {'request':request}) self.assertEqual(request.response.content_type, 'text/mishmash') def test_with_custom_adapter(self): request = testing.DummyRequest() from datetime import datetime def adapter(obj, req): self.assertEqual(req, request) return obj.isoformat() now = datetime.utcnow() renderer = self._makeOne() renderer.add_adapter(datetime, adapter) result = renderer(None)({'a':now}, {'request':request}) self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) def test_with_custom_adapter2(self): request = testing.DummyRequest() from datetime import datetime def adapter(obj, req): self.assertEqual(req, request) return obj.isoformat() now = datetime.utcnow() renderer = self._makeOne(adapters=((datetime, adapter),)) result = renderer(None)({'a':now}, {'request':request}) self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) def test_with_custom_serializer(self): class Serializer(object): def __call__(self, obj, **kw): self.obj = obj self.kw = kw return 'foo' serializer = Serializer() renderer = self._makeOne(serializer=serializer, baz=5) obj = {'a':'b'} result = renderer(None)(obj, {}) self.assertEqual(result, 'foo') self.assertEqual(serializer.obj, obj) self.assertEqual(serializer.kw['baz'], 5) self.assertTrue('default' in serializer.kw) def test_with_object_adapter(self): request = testing.DummyRequest() outerself = self class MyObject(object): def __init__(self, x): self.x = x def __json__(self, req): outerself.assertEqual(req, request) return {'x': self.x} objects = [MyObject(1), MyObject(2)] renderer = self._makeOne()(None) result = renderer(objects, {'request':request}) self.assertEqual(result, '[{"x": 1}, {"x": 2}]') def test_with_object_adapter_no___json__(self): class MyObject(object): def __init__(self, x): self.x = x objects = [MyObject(1), MyObject(2)] renderer = self._makeOne()(None) self.assertRaises(TypeError, renderer, objects, {}) class Test_string_renderer_factory(unittest.TestCase): def _callFUT(self, name): from pyramid.renderers import string_renderer_factory return string_renderer_factory(name) def test_it_unicode(self): renderer = self._callFUT(None) value = text_('La Pe\xc3\xb1a', 'utf-8') result = renderer(value, {}) self.assertEqual(result, value) def test_it_str(self): renderer = self._callFUT(None) value = 'La Pe\xc3\xb1a' result = renderer(value, {}) self.assertEqual(result, value) def test_it_other(self): renderer = self._callFUT(None) value = None result = renderer(value, {}) self.assertEqual(result, 'None') def test_with_request_content_type_notset(self): request = testing.DummyRequest() renderer = self._callFUT(None) renderer('', {'request':request}) self.assertEqual(request.response.content_type, 'text/plain') def test_with_request_content_type_set(self): request = testing.DummyRequest() request.response.content_type = 'text/mishmash' renderer = self._callFUT(None) renderer('', {'request':request}) self.assertEqual(request.response.content_type, 'text/mishmash') class TestRendererHelper(unittest.TestCase): def setUp(self): self.config = cleanUp() def tearDown(self): cleanUp() def _makeOne(self, *arg, **kw): from pyramid.renderers import RendererHelper return RendererHelper(*arg, **kw) def test_instance_conforms(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IRendererInfo helper = self._makeOne() verifyObject(IRendererInfo, helper) def test_settings_registry_settings_is_None(self): class Dummy(object): settings = None helper = self._makeOne(registry=Dummy) self.assertEqual(helper.settings, {}) def test_settings_registry_name_is_None(self): class Dummy(object): settings = None helper = self._makeOne(registry=Dummy) self.assertEqual(helper.name, None) self.assertEqual(helper.type, '') def test_settings_registry_settings_is_not_None(self): class Dummy(object): settings = {'a':1} helper = self._makeOne(registry=Dummy) self.assertEqual(helper.settings, {'a':1}) def _registerRendererFactory(self): from pyramid.interfaces import IRendererFactory def renderer(*arg): def respond(*arg): return arg renderer.respond = respond return respond self.config.registry.registerUtility(renderer, IRendererFactory, name='.foo') return renderer def _registerResponseFactory(self): from pyramid.interfaces import IResponseFactory class ResponseFactory(object): pass self.config.registry.registerUtility(ResponseFactory, IResponseFactory) def test_render_to_response(self): self._registerRendererFactory() self._registerResponseFactory() request = Dummy() helper = self._makeOne('loo.foo') response = helper.render_to_response('values', {}, request=request) self.assertEqual(response.body[0], 'values') self.assertEqual(response.body[1], {}) def test_get_renderer(self): factory = self._registerRendererFactory() helper = self._makeOne('loo.foo') self.assertEqual(helper.get_renderer(), factory.respond) def test_render_view(self): self._registerRendererFactory() self._registerResponseFactory() request = Dummy() helper = self._makeOne('loo.foo') view = 'view' context = 'context' request = testing.DummyRequest() response = 'response' response = helper.render_view(request, response, view, context) self.assertEqual(response.body[0], 'response') self.assertEqual(response.body[1], {'renderer_info': helper, 'renderer_name': 'loo.foo', 'request': request, 'context': 'context', 'view': 'view', 'req': request,} ) def test_render_explicit_registry(self): factory = self._registerRendererFactory() class DummyRegistry(object): def __init__(self): self.responses = [factory, lambda *arg: {}, None] def queryUtility(self, iface, name=None): self.queried = True return self.responses.pop(0) def notify(self, event): self.event = event reg = DummyRegistry() helper = self._makeOne('loo.foo', registry=reg) result = helper.render('value', {}) self.assertEqual(result[0], 'value') self.assertEqual(result[1], {}) self.assertTrue(reg.queried) self.assertEqual(reg.event, {}) self.assertEqual(reg.event.__class__.__name__, 'BeforeRender') def test_render_system_values_is_None(self): self._registerRendererFactory() request = Dummy() context = Dummy() request.context = context helper = self._makeOne('loo.foo') result = helper.render('values', None, request=request) system = {'request':request, 'context':context, 'renderer_name':'loo.foo', 'view':None, 'renderer_info':helper, 'req':request, } self.assertEqual(result[0], 'values') self.assertEqual(result[1], system) def test_render_renderer_globals_factory_active(self): self._registerRendererFactory() from pyramid.interfaces import IRendererGlobalsFactory def rg(system): return {'a':1} self.config.registry.registerUtility(rg, IRendererGlobalsFactory) helper = self._makeOne('loo.foo') result = helper.render('values', None) self.assertEqual(result[1]['a'], 1) def test__make_response_request_is_None(self): request = None helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.body, b'abc') def test__make_response_request_is_None_response_factory_exists(self): self._registerResponseFactory() request = None helper = self._makeOne('loo.foo') response = helper._make_response(b'abc', request) self.assertEqual(response.__class__.__name__, 'ResponseFactory') self.assertEqual(response.body, b'abc') def test__make_response_result_is_unicode(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() helper = self._makeOne('loo.foo') la = text_('/La Pe\xc3\xb1a', 'utf-8') response = helper._make_response(la, request) self.assertEqual(response.body, la.encode('utf-8')) def test__make_response_result_is_str(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() helper = self._makeOne('loo.foo') la = text_('/La Pe\xc3\xb1a', 'utf-8') response = helper._make_response(la.encode('utf-8'), request) self.assertEqual(response.body, la.encode('utf-8')) def test__make_response_result_is_None_no_body(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() helper = self._makeOne('loo.foo') response = helper._make_response(None, request) self.assertEqual(response.body, b'') def test__make_response_result_is_None_existing_body_not_molested(self): from pyramid.response import Response request = testing.DummyRequest() response = Response() response.body = b'abc' request.response = response helper = self._makeOne('loo.foo') response = helper._make_response(None, request) self.assertEqual(response.body, b'abc') def test__make_response_with_content_type(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() attrs = {'_response_content_type':'text/nonsense'} request.__dict__.update(attrs) helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.content_type, 'text/nonsense') self.assertEqual(response.body, b'abc') def test__make_response_with_headerlist(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() attrs = {'_response_headerlist':[('a', '1'), ('b', '2')]} request.__dict__.update(attrs) helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.headerlist, [('Content-Type', 'text/html; charset=UTF-8'), ('Content-Length', '3'), ('a', '1'), ('b', '2')]) self.assertEqual(response.body, b'abc') def test__make_response_with_status(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() attrs = {'_response_status':'406 You Lose'} request.__dict__.update(attrs) helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.status, '406 You Lose') self.assertEqual(response.body, b'abc') def test__make_response_with_charset(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() attrs = {'_response_charset':'UTF-16'} request.__dict__.update(attrs) helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.charset, 'UTF-16') def test__make_response_with_cache_for(self): from pyramid.response import Response request = testing.DummyRequest() request.response = Response() attrs = {'_response_cache_for':100} request.__dict__.update(attrs) helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.cache_control.max_age, 100) def test_with_alternate_response_factory(self): from pyramid.interfaces import IResponseFactory class ResponseFactory(object): def __init__(self): pass self.config.registry.registerUtility(ResponseFactory, IResponseFactory) request = testing.DummyRequest() helper = self._makeOne('loo.foo') response = helper._make_response(b'abc', request) self.assertEqual(response.__class__, ResponseFactory) self.assertEqual(response.body, b'abc') def test__make_response_with_real_request(self): # functional from pyramid.request import Request request = Request({}) request.registry = self.config.registry request.response.status = '406 You Lose' helper = self._makeOne('loo.foo') response = helper._make_response('abc', request) self.assertEqual(response.status, '406 You Lose') self.assertEqual(response.body, b'abc') def test_clone_noargs(self): helper = self._makeOne('name', 'package', 'registry') cloned_helper = helper.clone() self.assertEqual(cloned_helper.name, 'name') self.assertEqual(cloned_helper.package, 'package') self.assertEqual(cloned_helper.registry, 'registry') self.assertFalse(helper is cloned_helper) def test_clone_allargs(self): helper = self._makeOne('name', 'package', 'registry') cloned_helper = helper.clone(name='name2', package='package2', registry='registry2') self.assertEqual(cloned_helper.name, 'name2') self.assertEqual(cloned_helper.package, 'package2') self.assertEqual(cloned_helper.registry, 'registry2') self.assertFalse(helper is cloned_helper) def test_renderer_absolute_file(self): registry = self.config.registry settings = {} registry.settings = settings from pyramid.interfaces import IRendererFactory import os here = os.path.dirname(os.path.abspath(__file__)) fixture = os.path.join(here, 'fixtures/minimal.pt') def factory(info, **kw): return info self.config.registry.registerUtility( factory, IRendererFactory, name='.pt') result = self._makeOne(fixture).renderer self.assertEqual(result.registry, registry) self.assertEqual(result.type, '.pt') self.assertEqual(result.package, None) self.assertEqual(result.name, fixture) self.assertEqual(result.settings, settings) def test_renderer_with_package(self): import pyramid registry = self.config.registry settings = {} registry.settings = settings from pyramid.interfaces import IRendererFactory import os here = os.path.dirname(os.path.abspath(__file__)) fixture = os.path.join(here, 'fixtures/minimal.pt') def factory(info, **kw): return info self.config.registry.registerUtility( factory, IRendererFactory, name='.pt') result = self._makeOne(fixture, pyramid).renderer self.assertEqual(result.registry, registry) self.assertEqual(result.type, '.pt') self.assertEqual(result.package, pyramid) self.assertEqual(result.name, fixture) self.assertEqual(result.settings, settings) def test_renderer_missing(self): inst = self._makeOne('foo') self.assertRaises(ValueError, getattr, inst, 'renderer') class TestNullRendererHelper(unittest.TestCase): def setUp(self): self.config = cleanUp() def tearDown(self): cleanUp() def _makeOne(self, *arg, **kw): from pyramid.renderers import NullRendererHelper return NullRendererHelper(*arg, **kw) def test_instance_conforms(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IRendererInfo helper = self._makeOne() verifyObject(IRendererInfo, helper) def test_render_view(self): helper = self._makeOne() self.assertEqual(helper.render_view(None, True, None, None), True) def test_render(self): helper = self._makeOne() self.assertEqual(helper.render(True, None, None), True) def test_render_to_response(self): helper = self._makeOne() self.assertEqual(helper.render_to_response(True, None, None), True) def test_clone(self): helper = self._makeOne() self.assertTrue(helper.clone() is helper) class Test_render(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _callFUT(self, renderer_name, value, request=None, package=None): from pyramid.renderers import render return render(renderer_name, value, request=request, package=package) def test_it_no_request(self): renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') renderer.string_response = 'abc' result = self._callFUT('abc/def.pt', dict(a=1)) self.assertEqual(result, 'abc') renderer.assert_(a=1) renderer.assert_(request=None) def test_it_with_request(self): renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') renderer.string_response = 'abc' request = testing.DummyRequest() result = self._callFUT('abc/def.pt', dict(a=1), request=request) self.assertEqual(result, 'abc') renderer.assert_(a=1) renderer.assert_(request=request) def test_it_with_package(self): import pyramid.tests renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') renderer.string_response = 'abc' request = testing.DummyRequest() result = self._callFUT('abc/def.pt', dict(a=1), request=request, package=pyramid.tests) self.assertEqual(result, 'abc') renderer.assert_(a=1) renderer.assert_(request=request) class Test_render_to_response(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _callFUT(self, renderer_name, value, request=None, package=None): from pyramid.renderers import render_to_response return render_to_response(renderer_name, value, request=request, package=package) def test_it_no_request(self): renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') renderer.string_response = 'abc' response = self._callFUT('abc/def.pt', dict(a=1)) self.assertEqual(response.body, b'abc') renderer.assert_(a=1) renderer.assert_(request=None) def test_it_with_request(self): renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') renderer.string_response = 'abc' request = testing.DummyRequest() response = self._callFUT('abc/def.pt', dict(a=1), request=request) self.assertEqual(response.body, b'abc') renderer.assert_(a=1) renderer.assert_(request=request) def test_it_with_package(self): import pyramid.tests renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') renderer.string_response = 'abc' request = testing.DummyRequest() response = self._callFUT('abc/def.pt', dict(a=1), request=request, package=pyramid.tests) self.assertEqual(response.body, b'abc') renderer.assert_(a=1) renderer.assert_(request=request) class Test_get_renderer(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _callFUT(self, renderer_name, **kw): from pyramid.renderers import get_renderer return get_renderer(renderer_name, **kw) def test_it_no_package(self): renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') result = self._callFUT('abc/def.pt') self.assertEqual(result, renderer) def test_it_with_package(self): import pyramid.tests renderer = self.config.testing_add_renderer( 'pyramid.tests:abc/def.pt') result = self._callFUT('abc/def.pt', package=pyramid.tests) self.assertEqual(result, renderer) class TestJSONP(unittest.TestCase): def _makeOne(self, param_name='callback'): from pyramid.renderers import JSONP return JSONP(param_name) def test_render_to_jsonp(self): renderer_factory = self._makeOne() renderer = renderer_factory(None) request = testing.DummyRequest() request.GET['callback'] = 'callback' result = renderer({'a':'1'}, {'request':request}) self.assertEqual(result, 'callback({"a": "1"})') self.assertEqual(request.response.content_type, 'application/javascript') def test_render_to_json(self): renderer_factory = self._makeOne() renderer = renderer_factory(None) request = testing.DummyRequest() result = renderer({'a':'1'}, {'request':request}) self.assertEqual(result, '{"a": "1"}') self.assertEqual(request.response.content_type, 'application/json') class Dummy: pass class DummyResponse: status = '200 OK' headerlist = () app_iter = () body = '' class DummyFactory: def __init__(self, renderer): self.renderer = renderer def __call__(self, path, lookup, **kw): self.path = path self.kw = kw return self.renderer class DummyRendererInfo(object): def __init__(self, kw): self.__dict__.update(kw) pyramid-1.4.5/pyramid/tests/test_chameleon_text.py0000664000175000017500000001214712203712502021700 0ustar takakitakakiimport sys import unittest from pyramid.compat import binary_type from pyramid import testing class Base(object): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _getTemplatePath(self, name): import os here = os.path.abspath(os.path.dirname(__file__)) return os.path.join(here, 'fixtures', name) class Test_renderer_factory(Base, unittest.TestCase): def _callFUT(self, info): from pyramid.chameleon_text import renderer_factory return renderer_factory(info) def test_it(self): # this test is way too functional from pyramid.chameleon_text import TextTemplateRenderer info = DummyInfo() result = self._callFUT(info) self.assertEqual(result.__class__, TextTemplateRenderer) class TextTemplateRendererTests(Base, unittest.TestCase): def _getTargetClass(self): from pyramid.chameleon_text import TextTemplateRenderer return TextTemplateRenderer def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_instance_implements_ITemplate(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ITemplateRenderer path = self._getTemplatePath('minimal.txt') lookup = DummyLookup() verifyObject(ITemplateRenderer, self._makeOne(path, lookup)) def test_class_implements_ITemplate(self): from zope.interface.verify import verifyClass from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) def test_template_reified(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template, instance.__dict__['template']) def test_template_with_ichameleon_translate(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.translate, lookup.translate) def test_template_with_debug_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() lookup.debug = True instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.debug, True) def test_template_with_reload_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() lookup.auto_reload = True instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.auto_reload, True) def test_template_without_reload_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() lookup.auto_reload = False instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.auto_reload, False) def test_call(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) result = instance({}, {}) self.assertTrue(isinstance(result, binary_type)) self.assertEqual(result, b'Hello.\n') def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) def test_call_nonminimal(self): nonminimal = self._getTemplatePath('nonminimal.txt') lookup = DummyLookup() instance = self._makeOne(nonminimal, lookup) result = instance({'name':'Chris'}, {}) self.assertTrue(isinstance(result, binary_type)) self.assertEqual(result, b'Hello, Chris!\n') def test_implementation(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) result = instance.implementation()() self.assertTrue(isinstance(result, binary_type)) self.assertEqual(result, b'Hello.\n') class DummyLookup(object): auto_reload=True debug = True def translate(self, msg): pass class DummyRegistry(object): def queryUtility(self, iface, name): self.queried = iface, name return None def registerUtility(self, impl, iface, name): self.registered = impl, iface, name class DummyInfo(object): def __init__(self): self.registry = DummyRegistry() self.type = '.pt' self.name = 'fixtures/minimal.pt' self.package = sys.modules[__name__] self.settings = {} pyramid-1.4.5/pyramid/tests/test_docs.py0000664000175000017500000000223412203712502017625 0ustar takakitakakiimport unittest if 0: # no released version of manuel actually works with :lineno: # settings yet class ManuelDocsCase(unittest.TestCase): def __new__(self, test): return getattr(self, test)() @classmethod def test_docs(cls): import os import pkg_resources import manuel.testing import manuel.codeblock import manuel.capture import manuel.ignore m = manuel.ignore.Manuel() m += manuel.codeblock.Manuel() m += manuel.capture.Manuel() docs = [] egg_path = pkg_resources.get_distribution('pyramid').location path = os.path.join(egg_path, 'docs') for root, dirs, files in os.walk(path): for ignore in ('.svn', '.build', '.hg', '.git', 'CVS'): if ignore in dirs: dirs.remove(ignore) for filename in files: if filename.endswith('.rst'): docs.append(os.path.join(root, filename)) print(path) return manuel.testing.TestSuite(m, *docs) pyramid-1.4.5/pyramid/tests/test_config/0000775000175000017500000000000012210157153017572 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/test_util.py0000664000175000017500000004773412210154276022202 0ustar takakitakakiimport unittest from pyramid.compat import text_ class TestPredicateList(unittest.TestCase): def _makeOne(self): from pyramid.config.util import PredicateList from pyramid.config import predicates inst = PredicateList() for name, factory in ( ('xhr', predicates.XHRPredicate), ('request_method', predicates.RequestMethodPredicate), ('path_info', predicates.PathInfoPredicate), ('request_param', predicates.RequestParamPredicate), ('header', predicates.HeaderPredicate), ('accept', predicates.AcceptPredicate), ('containment', predicates.ContainmentPredicate), ('request_type', predicates.RequestTypePredicate), ('match_param', predicates.MatchParamPredicate), ('custom', predicates.CustomPredicate), ('traverse', predicates.TraversePredicate), ): inst.add(name, factory) return inst def _callFUT(self, **kw): inst = self._makeOne() config = DummyConfigurator() return inst.make(config, **kw) def test_ordering_xhr_and_request_method_trump_only_containment(self): order1, _, _ = self._callFUT(xhr=True, request_method='GET') order2, _, _ = self._callFUT(containment=True) self.assertTrue(order1 < order2) def test_ordering_number_of_predicates(self): from pyramid.config.util import predvalseq order1, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', header='header', accept='accept', containment='containment', request_type='request_type', custom=predvalseq([DummyCustomPredicate()]), ) order2, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', header='header', accept='accept', containment='containment', request_type='request_type', custom=predvalseq([DummyCustomPredicate()]), ) order3, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', header='header', accept='accept', containment='containment', request_type='request_type', ) order4, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', header='header', accept='accept', containment='containment', ) order5, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', header='header', accept='accept', ) order6, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', header='header', ) order7, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', match_param='foo=bar', ) order8, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', ) order9, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', ) order10, _, _ = self._callFUT( xhr='xhr', request_method='request_method', ) order11, _, _ = self._callFUT( xhr='xhr', ) order12, _, _ = self._callFUT( ) self.assertEqual(order1, order2) self.assertTrue(order3 > order2) self.assertTrue(order4 > order3) self.assertTrue(order5 > order4) self.assertTrue(order6 > order5) self.assertTrue(order7 > order6) self.assertTrue(order8 > order7) self.assertTrue(order9 > order8) self.assertTrue(order10 > order9) self.assertTrue(order11 > order10) self.assertTrue(order12 > order10) def test_ordering_importance_of_predicates(self): from pyramid.config.util import predvalseq order1, _, _ = self._callFUT( xhr='xhr', ) order2, _, _ = self._callFUT( request_method='request_method', ) order3, _, _ = self._callFUT( path_info='path_info', ) order4, _, _ = self._callFUT( request_param='param', ) order5, _, _ = self._callFUT( header='header', ) order6, _, _ = self._callFUT( accept='accept', ) order7, _, _ = self._callFUT( containment='containment', ) order8, _, _ = self._callFUT( request_type='request_type', ) order9, _, _ = self._callFUT( match_param='foo=bar', ) order10, _, _ = self._callFUT( custom=predvalseq([DummyCustomPredicate()]), ) self.assertTrue(order1 > order2) self.assertTrue(order2 > order3) self.assertTrue(order3 > order4) self.assertTrue(order4 > order5) self.assertTrue(order5 > order6) self.assertTrue(order6 > order7) self.assertTrue(order7 > order8) self.assertTrue(order8 > order9) self.assertTrue(order9 > order10) def test_ordering_importance_and_number(self): from pyramid.config.util import predvalseq order1, _, _ = self._callFUT( xhr='xhr', request_method='request_method', ) order2, _, _ = self._callFUT( custom=predvalseq([DummyCustomPredicate()]), ) self.assertTrue(order1 < order2) order1, _, _ = self._callFUT( xhr='xhr', request_method='request_method', ) order2, _, _ = self._callFUT( request_method='request_method', custom=predvalseq([DummyCustomPredicate()]), ) self.assertTrue(order1 > order2) order1, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', ) order2, _, _ = self._callFUT( request_method='request_method', custom=predvalseq([DummyCustomPredicate()]), ) self.assertTrue(order1 < order2) order1, _, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', ) order2, _, _ = self._callFUT( xhr='xhr', request_method='request_method', custom=predvalseq([DummyCustomPredicate()]), ) self.assertTrue(order1 > order2) def test_different_custom_predicates_with_same_hash(self): from pyramid.config.util import predvalseq class PredicateWithHash(object): def __hash__(self): return 1 a = PredicateWithHash() b = PredicateWithHash() _, _, a_phash = self._callFUT(custom=predvalseq([a])) _, _, b_phash = self._callFUT(custom=predvalseq([b])) self.assertEqual(a_phash, b_phash) def test_traverse_has_remainder_already(self): order, predicates, phash = self._callFUT(traverse='/1/:a/:b') self.assertEqual(len(predicates), 1) pred = predicates[0] info = {'traverse':'abc'} request = DummyRequest() result = pred(info, request) self.assertEqual(result, True) self.assertEqual(info, {'traverse':'abc'}) def test_traverse_matches(self): order, predicates, phash = self._callFUT(traverse='/1/:a/:b') self.assertEqual(len(predicates), 1) pred = predicates[0] info = {'match':{'a':'a', 'b':'b'}} request = DummyRequest() result = pred(info, request) self.assertEqual(result, True) self.assertEqual(info, {'match': {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) def test_traverse_matches_with_highorder_chars(self): order, predicates, phash = self._callFUT( traverse=text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8')) self.assertEqual(len(predicates), 1) pred = predicates[0] info = {'match':{'x':text_(b'Qu\xc3\xa9bec', 'utf-8')}} request = DummyRequest() result = pred(info, request) self.assertEqual(result, True) self.assertEqual( info['match']['traverse'], (text_(b'La Pe\xc3\xb1a', 'utf-8'), text_(b'Qu\xc3\xa9bec', 'utf-8')) ) def test_custom_predicates_can_affect_traversal(self): from pyramid.config.util import predvalseq def custom(info, request): m = info['match'] m['dummy'] = 'foo' return True _, predicates, _ = self._callFUT( custom=predvalseq([custom]), traverse='/1/:dummy/:a') self.assertEqual(len(predicates), 2) info = {'match':{'a':'a'}} request = DummyRequest() self.assertTrue(all([p(info, request) for p in predicates])) self.assertEqual(info, {'match': {'a':'a', 'dummy':'foo', 'traverse':('1', 'foo', 'a')}}) def test_predicate_text_is_correct(self): from pyramid.config.util import predvalseq _, predicates, _ = self._callFUT( xhr='xhr', request_method='request_method', path_info='path_info', request_param='param', header='header', accept='accept', containment='containment', request_type='request_type', custom=predvalseq( [ DummyCustomPredicate(), DummyCustomPredicate.classmethod_predicate, DummyCustomPredicate.classmethod_predicate_no_text, ] ), match_param='foo=bar') self.assertEqual(predicates[0].text(), 'xhr = True') self.assertEqual(predicates[1].text(), "request_method = request_method") self.assertEqual(predicates[2].text(), 'path_info = path_info') self.assertEqual(predicates[3].text(), 'request_param param') self.assertEqual(predicates[4].text(), 'header header') self.assertEqual(predicates[5].text(), 'accept = accept') self.assertEqual(predicates[6].text(), 'containment = containment') self.assertEqual(predicates[7].text(), 'request_type = request_type') self.assertEqual(predicates[8].text(), "match_param foo=bar") self.assertEqual(predicates[9].text(), 'custom predicate') self.assertEqual(predicates[10].text(), 'classmethod predicate') self.assertTrue(predicates[11].text().startswith('custom predicate')) def test_match_param_from_string(self): _, predicates, _ = self._callFUT(match_param='foo=bar') request = DummyRequest() request.matchdict = {'foo':'bar', 'baz':'bum'} self.assertTrue(predicates[0](Dummy(), request)) def test_match_param_from_string_fails(self): _, predicates, _ = self._callFUT(match_param='foo=bar') request = DummyRequest() request.matchdict = {'foo':'bum', 'baz':'bum'} self.assertFalse(predicates[0](Dummy(), request)) def test_match_param_from_dict(self): _, predicates, _ = self._callFUT(match_param=('foo=bar','baz=bum')) request = DummyRequest() request.matchdict = {'foo':'bar', 'baz':'bum'} self.assertTrue(predicates[0](Dummy(), request)) def test_match_param_from_dict_fails(self): _, predicates, _ = self._callFUT(match_param=('foo=bar','baz=bum')) request = DummyRequest() request.matchdict = {'foo':'bar', 'baz':'foo'} self.assertFalse(predicates[0](Dummy(), request)) def test_request_method_sequence(self): _, predicates, _ = self._callFUT(request_method=('GET', 'HEAD')) request = DummyRequest() request.method = 'HEAD' self.assertTrue(predicates[0](Dummy(), request)) request.method = 'GET' self.assertTrue(predicates[0](Dummy(), request)) request.method = 'POST' self.assertFalse(predicates[0](Dummy(), request)) def test_request_method_ordering_hashes_same(self): hash1, _, __= self._callFUT(request_method=('GET', 'HEAD')) hash2, _, __= self._callFUT(request_method=('HEAD', 'GET')) self.assertEqual(hash1, hash2) hash1, _, __= self._callFUT(request_method=('GET',)) hash2, _, __= self._callFUT(request_method='GET') self.assertEqual(hash1, hash2) def test_unknown_predicate(self): from pyramid.exceptions import ConfigurationError self.assertRaises(ConfigurationError, self._callFUT, unknown=1) class Test_takes_one_arg(unittest.TestCase): def _callFUT(self, view, attr=None, argname=None): from pyramid.config.util import takes_one_arg return takes_one_arg(view, attr=attr, argname=argname) def test_requestonly_newstyle_class_no_init(self): class foo(object): """ """ self.assertFalse(self._callFUT(foo)) def test_requestonly_newstyle_class_init_toomanyargs(self): class foo(object): def __init__(self, context, request): """ """ self.assertFalse(self._callFUT(foo)) def test_requestonly_newstyle_class_init_onearg_named_request(self): class foo(object): def __init__(self, request): """ """ self.assertTrue(self._callFUT(foo)) def test_newstyle_class_init_onearg_named_somethingelse(self): class foo(object): def __init__(self, req): """ """ self.assertTrue(self._callFUT(foo)) def test_newstyle_class_init_defaultargs_firstname_not_request(self): class foo(object): def __init__(self, context, request=None): """ """ self.assertFalse(self._callFUT(foo)) def test_newstyle_class_init_defaultargs_firstname_request(self): class foo(object): def __init__(self, request, foo=1, bar=2): """ """ self.assertTrue(self._callFUT(foo, argname='request')) def test_newstyle_class_init_firstname_request_with_secondname(self): class foo(object): def __init__(self, request, two): """ """ self.assertFalse(self._callFUT(foo)) def test_newstyle_class_init_noargs(self): class foo(object): def __init__(): """ """ self.assertFalse(self._callFUT(foo)) def test_oldstyle_class_no_init(self): class foo: """ """ self.assertFalse(self._callFUT(foo)) def test_oldstyle_class_init_toomanyargs(self): class foo: def __init__(self, context, request): """ """ self.assertFalse(self._callFUT(foo)) def test_oldstyle_class_init_onearg_named_request(self): class foo: def __init__(self, request): """ """ self.assertTrue(self._callFUT(foo)) def test_oldstyle_class_init_onearg_named_somethingelse(self): class foo: def __init__(self, req): """ """ self.assertTrue(self._callFUT(foo)) def test_oldstyle_class_init_defaultargs_firstname_not_request(self): class foo: def __init__(self, context, request=None): """ """ self.assertFalse(self._callFUT(foo)) def test_oldstyle_class_init_defaultargs_firstname_request(self): class foo: def __init__(self, request, foo=1, bar=2): """ """ self.assertTrue(self._callFUT(foo, argname='request'), True) def test_oldstyle_class_init_noargs(self): class foo: def __init__(): """ """ self.assertFalse(self._callFUT(foo)) def test_function_toomanyargs(self): def foo(context, request): """ """ self.assertFalse(self._callFUT(foo)) def test_function_with_attr_false(self): def bar(context, request): """ """ def foo(context, request): """ """ foo.bar = bar self.assertFalse(self._callFUT(foo, 'bar')) def test_function_with_attr_true(self): def bar(context, request): """ """ def foo(request): """ """ foo.bar = bar self.assertTrue(self._callFUT(foo, 'bar')) def test_function_onearg_named_request(self): def foo(request): """ """ self.assertTrue(self._callFUT(foo)) def test_function_onearg_named_somethingelse(self): def foo(req): """ """ self.assertTrue(self._callFUT(foo)) def test_function_defaultargs_firstname_not_request(self): def foo(context, request=None): """ """ self.assertFalse(self._callFUT(foo)) def test_function_defaultargs_firstname_request(self): def foo(request, foo=1, bar=2): """ """ self.assertTrue(self._callFUT(foo, argname='request')) def test_function_noargs(self): def foo(): """ """ self.assertFalse(self._callFUT(foo)) def test_instance_toomanyargs(self): class Foo: def __call__(self, context, request): """ """ foo = Foo() self.assertFalse(self._callFUT(foo)) def test_instance_defaultargs_onearg_named_request(self): class Foo: def __call__(self, request): """ """ foo = Foo() self.assertTrue(self._callFUT(foo)) def test_instance_defaultargs_onearg_named_somethingelse(self): class Foo: def __call__(self, req): """ """ foo = Foo() self.assertTrue(self._callFUT(foo)) def test_instance_defaultargs_firstname_not_request(self): class Foo: def __call__(self, context, request=None): """ """ foo = Foo() self.assertFalse(self._callFUT(foo)) def test_instance_defaultargs_firstname_request(self): class Foo: def __call__(self, request, foo=1, bar=2): """ """ foo = Foo() self.assertTrue(self._callFUT(foo, argname='request'), True) def test_instance_nocall(self): class Foo: pass foo = Foo() self.assertFalse(self._callFUT(foo)) def test_method_onearg_named_request(self): class Foo: def method(self, request): """ """ foo = Foo() self.assertTrue(self._callFUT(foo.method)) class DummyCustomPredicate(object): def __init__(self): self.__text__ = 'custom predicate' def classmethod_predicate(*args): pass classmethod_predicate.__text__ = 'classmethod predicate' classmethod_predicate = classmethod(classmethod_predicate) @classmethod def classmethod_predicate_no_text(*args): pass # pragma: no cover class Dummy: pass class DummyRequest: subpath = () matchdict = None def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ self.params = {} self.cookies = {} class DummyConfigurator(object): def maybe_dotted(self, thing): return thing pyramid-1.4.5/pyramid/tests/test_config/test_settings.py0000664000175000017500000007133212203712502023046 0ustar takakitakakiimport unittest class TestSettingsConfiguratorMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test__set_settings_as_None(self): config = self._makeOne() settings = config._set_settings(None) self.assertTrue(settings) def test__set_settings_as_dictwithvalues(self): config = self._makeOne() settings = config._set_settings({'a':'1'}) self.assertEqual(settings['a'], '1') def test_get_settings_nosettings(self): from pyramid.registry import Registry reg = Registry() config = self._makeOne(reg) self.assertEqual(config.get_settings(), None) def test_get_settings_withsettings(self): settings = {'a':1} config = self._makeOne() config.registry.settings = settings self.assertEqual(config.get_settings(), settings) def test_add_settings_settings_already_registered(self): from pyramid.registry import Registry reg = Registry() config = self._makeOne(reg) config._set_settings({'a':1}) config.add_settings({'b':2}) settings = reg.settings self.assertEqual(settings['a'], 1) self.assertEqual(settings['b'], 2) def test_add_settings_settings_not_yet_registered(self): from pyramid.registry import Registry from pyramid.interfaces import ISettings reg = Registry() config = self._makeOne(reg) config.add_settings({'a':1}) settings = reg.getUtility(ISettings) self.assertEqual(settings['a'], 1) def test_add_settings_settings_None(self): from pyramid.registry import Registry from pyramid.interfaces import ISettings reg = Registry() config = self._makeOne(reg) config.add_settings(None, a=1) settings = reg.getUtility(ISettings) self.assertEqual(settings['a'], 1) class TestSettings(unittest.TestCase): def _getTargetClass(self): from pyramid.config.settings import Settings return Settings def _makeOne(self, d=None, environ=None): if environ is None: environ = {} klass = self._getTargetClass() return klass(d, _environ_=environ) def test_getattr_success(self): import warnings with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always') settings = self._makeOne({'reload_templates':False}) self.assertEqual(settings.reload_templates, False) self.assertEqual(len(w), 1) def test_getattr_fail(self): import warnings with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always') settings = self._makeOne({}) self.assertRaises(AttributeError, settings.__getattr__, 'wontexist') self.assertEqual(len(w), 0) def test_getattr_raises_attribute_error(self): settings = self._makeOne() self.assertRaises(AttributeError, settings.__getattr__, 'mykey') def test_noargs(self): settings = self._makeOne() self.assertEqual(settings['debug_authorization'], False) self.assertEqual(settings['debug_notfound'], False) self.assertEqual(settings['debug_routematch'], False) self.assertEqual(settings['reload_templates'], False) self.assertEqual(settings['reload_resources'], False) self.assertEqual(settings['pyramid.debug_authorization'], False) self.assertEqual(settings['pyramid.debug_notfound'], False) self.assertEqual(settings['pyramid.debug_routematch'], False) self.assertEqual(settings['pyramid.reload_templates'], False) self.assertEqual(settings['pyramid.reload_resources'], False) def test_prevent_http_cache(self): settings = self._makeOne({}) self.assertEqual(settings['prevent_http_cache'], False) self.assertEqual(settings['pyramid.prevent_http_cache'], False) result = self._makeOne({'prevent_http_cache':'false'}) self.assertEqual(result['prevent_http_cache'], False) self.assertEqual(result['pyramid.prevent_http_cache'], False) result = self._makeOne({'prevent_http_cache':'t'}) self.assertEqual(result['prevent_http_cache'], True) self.assertEqual(result['pyramid.prevent_http_cache'], True) result = self._makeOne({'prevent_http_cache':'1'}) self.assertEqual(result['prevent_http_cache'], True) self.assertEqual(result['pyramid.prevent_http_cache'], True) result = self._makeOne({'pyramid.prevent_http_cache':'t'}) self.assertEqual(result['prevent_http_cache'], True) self.assertEqual(result['pyramid.prevent_http_cache'], True) result = self._makeOne({}, {'PYRAMID_PREVENT_HTTP_CACHE':'1'}) self.assertEqual(result['prevent_http_cache'], True) self.assertEqual(result['pyramid.prevent_http_cache'], True) result = self._makeOne({'prevent_http_cache':'false', 'pyramid.prevent_http_cache':'1'}) self.assertEqual(result['prevent_http_cache'], True) self.assertEqual(result['pyramid.prevent_http_cache'], True) result = self._makeOne({'prevent_http_cache':'false', 'pyramid.prevent_http_cache':'f'}, {'PYRAMID_PREVENT_HTTP_CACHE':'1'}) self.assertEqual(result['prevent_http_cache'], True) self.assertEqual(result['pyramid.prevent_http_cache'], True) def test_reload_templates(self): settings = self._makeOne({}) self.assertEqual(settings['reload_templates'], False) self.assertEqual(settings['pyramid.reload_templates'], False) result = self._makeOne({'reload_templates':'false'}) self.assertEqual(result['reload_templates'], False) self.assertEqual(result['pyramid.reload_templates'], False) result = self._makeOne({'reload_templates':'t'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['pyramid.reload_templates'], True) result = self._makeOne({'reload_templates':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['pyramid.reload_templates'], True) result = self._makeOne({'pyramid.reload_templates':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['pyramid.reload_templates'], True) result = self._makeOne({}, {'PYRAMID_RELOAD_TEMPLATES':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['pyramid.reload_templates'], True) result = self._makeOne({'reload_templates':'false', 'pyramid.reload_templates':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['pyramid.reload_templates'], True) result = self._makeOne({'reload_templates':'false'}, {'PYRAMID_RELOAD_TEMPLATES':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['pyramid.reload_templates'], True) def test_reload_resources(self): # alias for reload_assets result = self._makeOne({}) self.assertEqual(result['reload_resources'], False) self.assertEqual(result['reload_assets'], False) self.assertEqual(result['pyramid.reload_resources'], False) self.assertEqual(result['pyramid.reload_assets'], False) result = self._makeOne({'reload_resources':'false'}) self.assertEqual(result['reload_resources'], False) self.assertEqual(result['reload_assets'], False) self.assertEqual(result['pyramid.reload_resources'], False) self.assertEqual(result['pyramid.reload_assets'], False) result = self._makeOne({'reload_resources':'t'}) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'reload_resources':'1'}) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'pyramid.reload_resources':'1'}) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({}, {'PYRAMID_RELOAD_RESOURCES':'1'}) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'reload_resources':'false', 'pyramid.reload_resources':'1'}) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'reload_resources':'false', 'pyramid.reload_resources':'false'}, {'PYRAMID_RELOAD_RESOURCES':'1'}) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) def test_reload_assets(self): # alias for reload_resources result = self._makeOne({}) self.assertEqual(result['reload_assets'], False) self.assertEqual(result['reload_resources'], False) self.assertEqual(result['pyramid.reload_assets'], False) self.assertEqual(result['pyramid.reload_resources'], False) result = self._makeOne({'reload_assets':'false'}) self.assertEqual(result['reload_resources'], False) self.assertEqual(result['reload_assets'], False) self.assertEqual(result['pyramid.reload_assets'], False) self.assertEqual(result['pyramid.reload_resources'], False) result = self._makeOne({'reload_assets':'t'}) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) result = self._makeOne({'reload_assets':'1'}) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) result = self._makeOne({'pyramid.reload_assets':'1'}) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) result = self._makeOne({}, {'PYRAMID_RELOAD_ASSETS':'1'}) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) result = self._makeOne({'reload_assets':'false', 'pyramid.reload_assets':'1'}) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) result = self._makeOne({'reload_assets':'false', 'pyramid.reload_assets':'false'}, {'PYRAMID_RELOAD_ASSETS':'1'}) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) self.assertEqual(result['pyramid.reload_resources'], True) def test_reload_all(self): result = self._makeOne({}) self.assertEqual(result['reload_templates'], False) self.assertEqual(result['reload_resources'], False) self.assertEqual(result['reload_assets'], False) self.assertEqual(result['pyramid.reload_templates'], False) self.assertEqual(result['pyramid.reload_resources'], False) self.assertEqual(result['pyramid.reload_assets'], False) result = self._makeOne({'reload_all':'false'}) self.assertEqual(result['reload_templates'], False) self.assertEqual(result['reload_resources'], False) self.assertEqual(result['reload_assets'], False) self.assertEqual(result['pyramid.reload_templates'], False) self.assertEqual(result['pyramid.reload_resources'], False) self.assertEqual(result['pyramid.reload_assets'], False) result = self._makeOne({'reload_all':'t'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_templates'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'reload_all':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_templates'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'pyramid.reload_all':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_templates'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({}, {'PYRAMID_RELOAD_ALL':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_templates'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'reload_all':'false', 'pyramid.reload_all':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_templates'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) result = self._makeOne({'reload_all':'false', 'pyramid.reload_all':'false'}, {'PYRAMID_RELOAD_ALL':'1'}) self.assertEqual(result['reload_templates'], True) self.assertEqual(result['reload_resources'], True) self.assertEqual(result['reload_assets'], True) self.assertEqual(result['pyramid.reload_templates'], True) self.assertEqual(result['pyramid.reload_resources'], True) self.assertEqual(result['pyramid.reload_assets'], True) def test_debug_authorization(self): result = self._makeOne({}) self.assertEqual(result['debug_authorization'], False) self.assertEqual(result['pyramid.debug_authorization'], False) result = self._makeOne({'debug_authorization':'false'}) self.assertEqual(result['debug_authorization'], False) self.assertEqual(result['pyramid.debug_authorization'], False) result = self._makeOne({'debug_authorization':'t'}) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['pyramid.debug_authorization'], True) result = self._makeOne({'debug_authorization':'1'}) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['pyramid.debug_authorization'], True) result = self._makeOne({'pyramid.debug_authorization':'1'}) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['pyramid.debug_authorization'], True) result = self._makeOne({}, {'PYRAMID_DEBUG_AUTHORIZATION':'1'}) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['pyramid.debug_authorization'], True) result = self._makeOne({'debug_authorization':'false', 'pyramid.debug_authorization':'1'}) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['pyramid.debug_authorization'], True) result = self._makeOne({'debug_authorization':'false', 'pyramid.debug_authorization':'false'}, {'PYRAMID_DEBUG_AUTHORIZATION':'1'}) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['pyramid.debug_authorization'], True) def test_debug_notfound(self): result = self._makeOne({}) self.assertEqual(result['debug_notfound'], False) self.assertEqual(result['pyramid.debug_notfound'], False) result = self._makeOne({'debug_notfound':'false'}) self.assertEqual(result['debug_notfound'], False) self.assertEqual(result['pyramid.debug_notfound'], False) result = self._makeOne({'debug_notfound':'t'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['pyramid.debug_notfound'], True) result = self._makeOne({'debug_notfound':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['pyramid.debug_notfound'], True) result = self._makeOne({'pyramid.debug_notfound':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['pyramid.debug_notfound'], True) result = self._makeOne({}, {'PYRAMID_DEBUG_NOTFOUND':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['pyramid.debug_notfound'], True) result = self._makeOne({'debug_notfound':'false', 'pyramid.debug_notfound':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['pyramid.debug_notfound'], True) result = self._makeOne({'debug_notfound':'false', 'pyramid.debug_notfound':'false'}, {'PYRAMID_DEBUG_NOTFOUND':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['pyramid.debug_notfound'], True) def test_debug_routematch(self): result = self._makeOne({}) self.assertEqual(result['debug_routematch'], False) self.assertEqual(result['pyramid.debug_routematch'], False) result = self._makeOne({'debug_routematch':'false'}) self.assertEqual(result['debug_routematch'], False) self.assertEqual(result['pyramid.debug_routematch'], False) result = self._makeOne({'debug_routematch':'t'}) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['pyramid.debug_routematch'], True) result = self._makeOne({'debug_routematch':'1'}) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['pyramid.debug_routematch'], True) result = self._makeOne({'pyramid.debug_routematch':'1'}) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['pyramid.debug_routematch'], True) result = self._makeOne({}, {'PYRAMID_DEBUG_ROUTEMATCH':'1'}) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['pyramid.debug_routematch'], True) result = self._makeOne({'debug_routematch':'false', 'pyramid.debug_routematch':'1'}) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['pyramid.debug_routematch'], True) result = self._makeOne({'debug_routematch':'false', 'pyramid.debug_routematch':'false'}, {'PYRAMID_DEBUG_ROUTEMATCH':'1'}) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['pyramid.debug_routematch'], True) def test_debug_templates(self): result = self._makeOne({}) self.assertEqual(result['debug_templates'], False) self.assertEqual(result['pyramid.debug_templates'], False) result = self._makeOne({'debug_templates':'false'}) self.assertEqual(result['debug_templates'], False) self.assertEqual(result['pyramid.debug_templates'], False) result = self._makeOne({'debug_templates':'t'}) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'debug_templates':'1'}) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'pyramid.debug_templates':'1'}) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({}, {'PYRAMID_DEBUG_TEMPLATES':'1'}) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'debug_templates':'false', 'pyramid.debug_templates':'1'}) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'debug_templates':'false', 'pyramid.debug_templates':'false'}, {'PYRAMID_DEBUG_TEMPLATES':'1'}) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_templates'], True) def test_debug_all(self): result = self._makeOne({}) self.assertEqual(result['debug_notfound'], False) self.assertEqual(result['debug_routematch'], False) self.assertEqual(result['debug_authorization'], False) self.assertEqual(result['debug_templates'], False) self.assertEqual(result['pyramid.debug_notfound'], False) self.assertEqual(result['pyramid.debug_routematch'], False) self.assertEqual(result['pyramid.debug_authorization'], False) self.assertEqual(result['pyramid.debug_templates'], False) result = self._makeOne({'debug_all':'false'}) self.assertEqual(result['debug_notfound'], False) self.assertEqual(result['debug_routematch'], False) self.assertEqual(result['debug_authorization'], False) self.assertEqual(result['debug_templates'], False) self.assertEqual(result['pyramid.debug_notfound'], False) self.assertEqual(result['pyramid.debug_routematch'], False) self.assertEqual(result['pyramid.debug_authorization'], False) self.assertEqual(result['pyramid.debug_templates'], False) result = self._makeOne({'debug_all':'t'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_notfound'], True) self.assertEqual(result['pyramid.debug_routematch'], True) self.assertEqual(result['pyramid.debug_authorization'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'debug_all':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_notfound'], True) self.assertEqual(result['pyramid.debug_routematch'], True) self.assertEqual(result['pyramid.debug_authorization'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'pyramid.debug_all':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_notfound'], True) self.assertEqual(result['pyramid.debug_routematch'], True) self.assertEqual(result['pyramid.debug_authorization'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({}, {'PYRAMID_DEBUG_ALL':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_notfound'], True) self.assertEqual(result['pyramid.debug_routematch'], True) self.assertEqual(result['pyramid.debug_authorization'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'debug_all':'false', 'pyramid.debug_all':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_notfound'], True) self.assertEqual(result['pyramid.debug_routematch'], True) self.assertEqual(result['pyramid.debug_authorization'], True) self.assertEqual(result['pyramid.debug_templates'], True) result = self._makeOne({'debug_all':'false', 'pyramid.debug_all':'false'}, {'PYRAMID_DEBUG_ALL':'1'}) self.assertEqual(result['debug_notfound'], True) self.assertEqual(result['debug_routematch'], True) self.assertEqual(result['debug_authorization'], True) self.assertEqual(result['debug_templates'], True) self.assertEqual(result['pyramid.debug_notfound'], True) self.assertEqual(result['pyramid.debug_routematch'], True) self.assertEqual(result['pyramid.debug_authorization'], True) self.assertEqual(result['pyramid.debug_templates'], True) def test_default_locale_name(self): result = self._makeOne({}) self.assertEqual(result['default_locale_name'], 'en') self.assertEqual(result['pyramid.default_locale_name'], 'en') result = self._makeOne({'default_locale_name':'abc'}) self.assertEqual(result['default_locale_name'], 'abc') self.assertEqual(result['pyramid.default_locale_name'], 'abc') result = self._makeOne({'pyramid.default_locale_name':'abc'}) self.assertEqual(result['default_locale_name'], 'abc') self.assertEqual(result['pyramid.default_locale_name'], 'abc') result = self._makeOne({}, {'PYRAMID_DEFAULT_LOCALE_NAME':'abc'}) self.assertEqual(result['default_locale_name'], 'abc') self.assertEqual(result['pyramid.default_locale_name'], 'abc') result = self._makeOne({'default_locale_name':'def', 'pyramid.default_locale_name':'abc'}) self.assertEqual(result['default_locale_name'], 'abc') self.assertEqual(result['pyramid.default_locale_name'], 'abc') result = self._makeOne({'default_locale_name':'def', 'pyramid.default_locale_name':'ghi'}, {'PYRAMID_DEFAULT_LOCALE_NAME':'abc'}) self.assertEqual(result['default_locale_name'], 'abc') self.assertEqual(result['pyramid.default_locale_name'], 'abc') def test_originals_kept(self): result = self._makeOne({'a':'i am so a'}) self.assertEqual(result['a'], 'i am so a') pyramid-1.4.5/pyramid/tests/test_config/test_init.py0000664000175000017500000023050412210154276022155 0ustar takakitakakiimport unittest import warnings import os from pyramid.compat import PYPY from pyramid.compat import im_func from pyramid.testing import skip_on from pyramid.tests.test_config import dummy_tween_factory from pyramid.tests.test_config import dummy_include from pyramid.tests.test_config import dummy_extend from pyramid.tests.test_config import dummy_extend2 from pyramid.tests.test_config import IDummy from pyramid.tests.test_config import DummyContext from pyramid.exceptions import ConfigurationExecutionError from pyramid.exceptions import ConfigurationConflictError class ConfiguratorTests(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def _getViewCallable(self, config, ctx_iface=None, request_iface=None, name='', exception_view=False): from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier if exception_view: # pragma: no cover classifier = IExceptionViewClassifier else: classifier = IViewClassifier if ctx_iface is None: ctx_iface = Interface if request_iface is None: request_iface = IRequest return config.registry.adapters.lookup( (classifier, request_iface, ctx_iface), IView, name=name, default=None) def _registerEventListener(self, config, event_iface=None): if event_iface is None: # pragma: no cover from zope.interface import Interface event_iface = Interface L = [] def subscriber(*event): L.extend(event) config.registry.registerHandler(subscriber, (event_iface,)) return L def _makeRequest(self, config): request = DummyRequest() request.registry = config.registry return request def test_ctor_no_registry(self): import sys from pyramid.interfaces import ISettings from pyramid.config import Configurator from pyramid.interfaces import IRendererFactory config = Configurator() this_pkg = sys.modules['pyramid.tests.test_config'] self.assertTrue(config.registry.getUtility(ISettings)) self.assertEqual(config.package, this_pkg) config.commit() self.assertTrue(config.registry.getUtility(IRendererFactory, 'json')) self.assertTrue(config.registry.getUtility(IRendererFactory, 'string')) if not PYPY: self.assertTrue(config.registry.getUtility(IRendererFactory, '.pt')) self.assertTrue(config.registry.getUtility(IRendererFactory,'.txt')) self.assertTrue(config.registry.getUtility(IRendererFactory, '.mak')) self.assertTrue(config.registry.getUtility(IRendererFactory, '.mako')) def test_begin(self): from pyramid.config import Configurator config = Configurator() manager = DummyThreadLocalManager() config.manager = manager config.begin() self.assertEqual(manager.pushed, {'registry':config.registry, 'request':None}) self.assertEqual(manager.popped, False) def test_begin_with_request(self): from pyramid.config import Configurator config = Configurator() request = object() manager = DummyThreadLocalManager() config.manager = manager config.begin(request=request) self.assertEqual(manager.pushed, {'registry':config.registry, 'request':request}) self.assertEqual(manager.popped, False) def test_end(self): from pyramid.config import Configurator config = Configurator() manager = DummyThreadLocalManager() config.manager = manager config.end() self.assertEqual(manager.pushed, None) self.assertEqual(manager.popped, True) def test_ctor_with_package_registry(self): import sys from pyramid.config import Configurator pkg = sys.modules['pyramid'] config = Configurator(package=pkg) self.assertEqual(config.package, pkg) def test_ctor_noreg_custom_settings(self): from pyramid.interfaces import ISettings settings = {'reload_templates':True, 'mysetting':True} config = self._makeOne(settings=settings) settings = config.registry.getUtility(ISettings) self.assertEqual(settings['reload_templates'], True) self.assertEqual(settings['debug_authorization'], False) self.assertEqual(settings['mysetting'], True) def test_ctor_noreg_debug_logger_None_default(self): from pyramid.interfaces import IDebugLogger config = self._makeOne() logger = config.registry.getUtility(IDebugLogger) self.assertEqual(logger.name, 'pyramid.tests.test_config') def test_ctor_noreg_debug_logger_non_None(self): from pyramid.interfaces import IDebugLogger logger = object() config = self._makeOne(debug_logger=logger) result = config.registry.getUtility(IDebugLogger) self.assertEqual(logger, result) def test_ctor_authentication_policy(self): from pyramid.interfaces import IAuthenticationPolicy policy = object() config = self._makeOne(authentication_policy=policy) config.commit() result = config.registry.getUtility(IAuthenticationPolicy) self.assertEqual(policy, result) def test_ctor_authorization_policy_only(self): policy = object() config = self._makeOne(authorization_policy=policy) self.assertRaises(ConfigurationExecutionError, config.commit) def test_ctor_no_root_factory(self): from pyramid.interfaces import IRootFactory config = self._makeOne() self.assertEqual(config.registry.queryUtility(IRootFactory), None) config.commit() self.assertEqual(config.registry.queryUtility(IRootFactory), None) def test_ctor_with_root_factory(self): from pyramid.interfaces import IRootFactory factory = object() config = self._makeOne(root_factory=factory) self.assertEqual(config.registry.queryUtility(IRootFactory), None) config.commit() self.assertEqual(config.registry.queryUtility(IRootFactory), factory) def test_ctor_alternate_renderers(self): from pyramid.interfaces import IRendererFactory renderer = object() config = self._makeOne(renderers=[('yeah', renderer)]) config.commit() self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), renderer) def test_ctor_default_renderers(self): from pyramid.interfaces import IRendererFactory from pyramid.renderers import json_renderer_factory config = self._makeOne() self.assertEqual(config.registry.getUtility(IRendererFactory, 'json'), json_renderer_factory) def test_ctor_default_permission(self): from pyramid.interfaces import IDefaultPermission config = self._makeOne(default_permission='view') config.commit() self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') def test_ctor_session_factory(self): from pyramid.interfaces import ISessionFactory factory = object() config = self._makeOne(session_factory=factory) self.assertEqual(config.registry.queryUtility(ISessionFactory), None) config.commit() self.assertEqual(config.registry.getUtility(ISessionFactory), factory) def test_ctor_default_view_mapper(self): from pyramid.interfaces import IViewMapperFactory mapper = object() config = self._makeOne(default_view_mapper=mapper) config.commit() self.assertEqual(config.registry.getUtility(IViewMapperFactory), mapper) def test_ctor_httpexception_view_default(self): from pyramid.interfaces import IExceptionResponse from pyramid.httpexceptions import default_exceptionresponse_view from pyramid.interfaces import IRequest config = self._makeOne() view = self._getViewCallable(config, ctx_iface=IExceptionResponse, request_iface=IRequest) self.assertTrue(view.__wraps__ is default_exceptionresponse_view) def test_ctor_exceptionresponse_view_None(self): from pyramid.interfaces import IExceptionResponse from pyramid.interfaces import IRequest config = self._makeOne(exceptionresponse_view=None) view = self._getViewCallable(config, ctx_iface=IExceptionResponse, request_iface=IRequest) self.assertTrue(view is None) def test_ctor_exceptionresponse_view_custom(self): from pyramid.interfaces import IExceptionResponse from pyramid.interfaces import IRequest def exceptionresponse_view(context, request): pass config = self._makeOne(exceptionresponse_view=exceptionresponse_view) view = self._getViewCallable(config, ctx_iface=IExceptionResponse, request_iface=IRequest) self.assertTrue(view.__wraps__ is exceptionresponse_view) def test_ctor_with_introspection(self): config = self._makeOne(introspection=False) self.assertEqual(config.introspection, False) def test_with_package_module(self): from pyramid.tests.test_config import test_init import pyramid.tests config = self._makeOne() newconfig = config.with_package(test_init) self.assertEqual(newconfig.package, pyramid.tests.test_config) def test_with_package_package(self): import pyramid.tests.test_config config = self._makeOne() newconfig = config.with_package(pyramid.tests.test_config) self.assertEqual(newconfig.package, pyramid.tests.test_config) def test_with_package(self): import pyramid.tests config = self._makeOne() config.basepath = 'basepath' config.info = 'info' config.includepath = ('spec',) config.autocommit = True config.route_prefix = 'prefix' newconfig = config.with_package(pyramid.tests) self.assertEqual(newconfig.package, pyramid.tests) self.assertEqual(newconfig.registry, config.registry) self.assertEqual(newconfig.autocommit, True) self.assertEqual(newconfig.route_prefix, 'prefix') self.assertEqual(newconfig.info, 'info') self.assertEqual(newconfig.basepath, 'basepath') self.assertEqual(newconfig.includepath, ('spec',)) def test_maybe_dotted_string_success(self): import pyramid.tests.test_config config = self._makeOne() result = config.maybe_dotted('pyramid.tests.test_config') self.assertEqual(result, pyramid.tests.test_config) def test_maybe_dotted_string_fail(self): config = self._makeOne() self.assertRaises(ImportError, config.maybe_dotted, 'cant.be.found') def test_maybe_dotted_notstring_success(self): import pyramid.tests.test_config config = self._makeOne() result = config.maybe_dotted(pyramid.tests.test_config) self.assertEqual(result, pyramid.tests.test_config) def test_absolute_asset_spec_already_absolute(self): import pyramid.tests.test_config config = self._makeOne(package=pyramid.tests.test_config) result = config.absolute_asset_spec('already:absolute') self.assertEqual(result, 'already:absolute') def test_absolute_asset_spec_notastring(self): import pyramid.tests.test_config config = self._makeOne(package=pyramid.tests.test_config) result = config.absolute_asset_spec(None) self.assertEqual(result, None) def test_absolute_asset_spec_relative(self): import pyramid.tests.test_config config = self._makeOne(package=pyramid.tests.test_config) result = config.absolute_asset_spec('files') self.assertEqual(result, 'pyramid.tests.test_config:files') def test__fix_registry_has_listeners(self): reg = DummyRegistry() config = self._makeOne(reg) config._fix_registry() self.assertEqual(reg.has_listeners, True) def test__fix_registry_notify(self): reg = DummyRegistry() config = self._makeOne(reg) config._fix_registry() self.assertEqual(reg.notify(1), None) self.assertEqual(reg.events, (1,)) def test__fix_registry_queryAdapterOrSelf(self): from zope.interface import Interface from zope.interface import implementer class IFoo(Interface): pass @implementer(IFoo) class Foo(object): pass class Bar(object): pass adaptation = () foo = Foo() bar = Bar() reg = DummyRegistry(adaptation) config = self._makeOne(reg) config._fix_registry() self.assertTrue(reg.queryAdapterOrSelf(foo, IFoo) is foo) self.assertTrue(reg.queryAdapterOrSelf(bar, IFoo) is adaptation) def test__fix_registry_registerSelfAdapter(self): reg = DummyRegistry() config = self._makeOne(reg) config._fix_registry() reg.registerSelfAdapter('required', 'provided', name='abc') self.assertEqual(len(reg.adapters), 1) args, kw = reg.adapters[0] self.assertEqual(args[0]('abc'), 'abc') self.assertEqual(kw, {'info': '', 'provided': 'provided', 'required': 'required', 'name': 'abc', 'event': True}) def test_setup_registry_calls_fix_registry(self): reg = DummyRegistry() config = self._makeOne(reg) config.add_view = lambda *arg, **kw: False config._add_tween = lambda *arg, **kw: False config.setup_registry() self.assertEqual(reg.has_listeners, True) def test_setup_registry_registers_default_exceptionresponse_views(self): from webob.exc import WSGIHTTPException from pyramid.interfaces import IExceptionResponse from pyramid.view import default_exceptionresponse_view reg = DummyRegistry() config = self._makeOne(reg) views = [] config.add_view = lambda *arg, **kw: views.append((arg, kw)) config.add_default_view_predicates = lambda *arg: None config._add_tween = lambda *arg, **kw: False config.setup_registry() self.assertEqual(views[0], ((default_exceptionresponse_view,), {'context':IExceptionResponse})) self.assertEqual(views[1], ((default_exceptionresponse_view,), {'context':WSGIHTTPException})) def test_setup_registry_registers_default_view_predicates(self): reg = DummyRegistry() config = self._makeOne(reg) vp_called = [] config.add_view = lambda *arg, **kw: None config.add_default_view_predicates = lambda *arg: vp_called.append(True) config._add_tween = lambda *arg, **kw: False config.setup_registry() self.assertTrue(vp_called) def test_setup_registry_registers_default_webob_iresponse_adapter(self): from webob import Response from pyramid.interfaces import IResponse config = self._makeOne() config.setup_registry() response = Response() self.assertTrue( config.registry.queryAdapter(response, IResponse) is response) def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.httpexceptions import HTTPNotFound from pyramid.registry import Registry reg = Registry() config = self._makeOne(reg, autocommit=True) config.setup_registry() # registers IExceptionResponse default view def myview(context, request): return 'OK' config.add_view(myview, context=HTTPNotFound, renderer=null_renderer) request = self._makeRequest(config) view = self._getViewCallable(config, ctx_iface=implementedBy(HTTPNotFound), request_iface=IRequest) result = view(None, request) self.assertEqual(result, 'OK') def test_setup_registry_custom_settings(self): from pyramid.registry import Registry from pyramid.interfaces import ISettings settings = {'reload_templates':True, 'mysetting':True} reg = Registry() config = self._makeOne(reg) config.setup_registry(settings=settings) settings = reg.getUtility(ISettings) self.assertEqual(settings['reload_templates'], True) self.assertEqual(settings['debug_authorization'], False) self.assertEqual(settings['mysetting'], True) def test_setup_registry_debug_logger_None_default(self): from pyramid.registry import Registry from pyramid.interfaces import IDebugLogger reg = Registry() config = self._makeOne(reg) config.setup_registry() logger = reg.getUtility(IDebugLogger) self.assertEqual(logger.name, 'pyramid.tests.test_config') def test_setup_registry_debug_logger_non_None(self): from pyramid.registry import Registry from pyramid.interfaces import IDebugLogger logger = object() reg = Registry() config = self._makeOne(reg) config.setup_registry(debug_logger=logger) result = reg.getUtility(IDebugLogger) self.assertEqual(logger, result) def test_setup_registry_debug_logger_name(self): from pyramid.registry import Registry from pyramid.interfaces import IDebugLogger reg = Registry() config = self._makeOne(reg) config.setup_registry(debug_logger='foo') result = reg.getUtility(IDebugLogger) self.assertEqual(result.name, 'foo') def test_setup_registry_authentication_policy(self): from pyramid.registry import Registry from pyramid.interfaces import IAuthenticationPolicy policy = object() reg = Registry() config = self._makeOne(reg) config.setup_registry(authentication_policy=policy) config.commit() result = reg.getUtility(IAuthenticationPolicy) self.assertEqual(policy, result) def test_setup_registry_authentication_policy_dottedname(self): from pyramid.registry import Registry from pyramid.interfaces import IAuthenticationPolicy reg = Registry() config = self._makeOne(reg) config.setup_registry(authentication_policy='pyramid.tests.test_config') config.commit() result = reg.getUtility(IAuthenticationPolicy) import pyramid.tests.test_config self.assertEqual(result, pyramid.tests.test_config) def test_setup_registry_authorization_policy_dottedname(self): from pyramid.registry import Registry from pyramid.interfaces import IAuthorizationPolicy reg = Registry() config = self._makeOne(reg) dummy = object() config.setup_registry(authentication_policy=dummy, authorization_policy='pyramid.tests.test_config') config.commit() result = reg.getUtility(IAuthorizationPolicy) import pyramid.tests.test_config self.assertEqual(result, pyramid.tests.test_config) def test_setup_registry_authorization_policy_only(self): from pyramid.registry import Registry policy = object() reg = Registry() config = self._makeOne(reg) config.setup_registry(authorization_policy=policy) config = self.assertRaises(ConfigurationExecutionError, config.commit) def test_setup_registry_no_default_root_factory(self): from pyramid.registry import Registry from pyramid.interfaces import IRootFactory reg = Registry() config = self._makeOne(reg) config.setup_registry() config.commit() self.assertEqual(reg.queryUtility(IRootFactory), None) def test_setup_registry_dottedname_root_factory(self): from pyramid.registry import Registry from pyramid.interfaces import IRootFactory reg = Registry() config = self._makeOne(reg) import pyramid.tests.test_config config.setup_registry(root_factory='pyramid.tests.test_config') self.assertEqual(reg.queryUtility(IRootFactory), None) config.commit() self.assertEqual(reg.getUtility(IRootFactory), pyramid.tests.test_config) def test_setup_registry_locale_negotiator_dottedname(self): from pyramid.registry import Registry from pyramid.interfaces import ILocaleNegotiator reg = Registry() config = self._makeOne(reg) import pyramid.tests.test_config config.setup_registry(locale_negotiator='pyramid.tests.test_config') self.assertEqual(reg.queryUtility(ILocaleNegotiator), None) config.commit() utility = reg.getUtility(ILocaleNegotiator) self.assertEqual(utility, pyramid.tests.test_config) def test_setup_registry_locale_negotiator(self): from pyramid.registry import Registry from pyramid.interfaces import ILocaleNegotiator reg = Registry() config = self._makeOne(reg) negotiator = object() config.setup_registry(locale_negotiator=negotiator) self.assertEqual(reg.queryUtility(ILocaleNegotiator), None) config.commit() utility = reg.getUtility(ILocaleNegotiator) self.assertEqual(utility, negotiator) def test_setup_registry_request_factory(self): from pyramid.registry import Registry from pyramid.interfaces import IRequestFactory reg = Registry() config = self._makeOne(reg) factory = object() config.setup_registry(request_factory=factory) self.assertEqual(reg.queryUtility(IRequestFactory), None) config.commit() utility = reg.getUtility(IRequestFactory) self.assertEqual(utility, factory) def test_setup_registry_request_factory_dottedname(self): from pyramid.registry import Registry from pyramid.interfaces import IRequestFactory reg = Registry() config = self._makeOne(reg) import pyramid.tests.test_config config.setup_registry(request_factory='pyramid.tests.test_config') self.assertEqual(reg.queryUtility(IRequestFactory), None) config.commit() utility = reg.getUtility(IRequestFactory) self.assertEqual(utility, pyramid.tests.test_config) def test_setup_registry_renderer_globals_factory(self): from pyramid.registry import Registry from pyramid.interfaces import IRendererGlobalsFactory reg = Registry() config = self._makeOne(reg) factory = object() with warnings.catch_warnings(): warnings.filterwarnings('ignore') config.setup_registry(renderer_globals_factory=factory) self.assertEqual(reg.queryUtility(IRendererGlobalsFactory), None) config.commit() utility = reg.getUtility(IRendererGlobalsFactory) self.assertEqual(utility, factory) def test_setup_registry_renderer_globals_factory_dottedname(self): from pyramid.registry import Registry from pyramid.interfaces import IRendererGlobalsFactory reg = Registry() config = self._makeOne(reg) import pyramid.tests.test_config with warnings.catch_warnings(): warnings.filterwarnings('ignore') config.setup_registry( renderer_globals_factory='pyramid.tests.test_config') self.assertEqual(reg.queryUtility(IRendererGlobalsFactory), None) config.commit() utility = reg.getUtility(IRendererGlobalsFactory) self.assertEqual(utility, pyramid.tests.test_config) def test_setup_registry_alternate_renderers(self): from pyramid.registry import Registry from pyramid.interfaces import IRendererFactory renderer = object() reg = Registry() config = self._makeOne(reg) config.setup_registry(renderers=[('yeah', renderer)]) config.commit() self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), renderer) def test_setup_registry_default_permission(self): from pyramid.registry import Registry from pyramid.interfaces import IDefaultPermission reg = Registry() config = self._makeOne(reg) config.setup_registry(default_permission='view') config.commit() self.assertEqual(reg.getUtility(IDefaultPermission), 'view') def test_setup_registry_includes(self): from pyramid.registry import Registry reg = Registry() config = self._makeOne(reg) settings = { 'pyramid.includes': """pyramid.tests.test_config.dummy_include pyramid.tests.test_config.dummy_include2""", } config.setup_registry(settings=settings) self.assertTrue(reg.included) self.assertTrue(reg.also_included) def test_setup_registry_includes_spaces(self): from pyramid.registry import Registry reg = Registry() config = self._makeOne(reg) settings = { 'pyramid.includes': """pyramid.tests.test_config.dummy_include pyramid.tests.test_config.dummy_include2""", } config.setup_registry(settings=settings) self.assertTrue(reg.included) self.assertTrue(reg.also_included) def test_setup_registry_tweens(self): from pyramid.interfaces import ITweens from pyramid.registry import Registry reg = Registry() config = self._makeOne(reg) settings = { 'pyramid.tweens': 'pyramid.tests.test_config.dummy_tween_factory' } config.setup_registry(settings=settings) config.commit() tweens = config.registry.getUtility(ITweens) self.assertEqual( tweens.explicit, [('pyramid.tests.test_config.dummy_tween_factory', dummy_tween_factory)]) def test_introspector_decorator(self): inst = self._makeOne() default = inst.introspector self.assertTrue(hasattr(default, 'add')) self.assertEqual(inst.introspector, inst.registry.introspector) introspector = object() inst.introspector = introspector new = inst.introspector self.assertTrue(new is introspector) self.assertEqual(inst.introspector, inst.registry.introspector) del inst.introspector default = inst.introspector self.assertFalse(default is new) self.assertTrue(hasattr(default, 'add')) def test_make_wsgi_app(self): import pyramid.config from pyramid.router import Router from pyramid.interfaces import IApplicationCreated manager = DummyThreadLocalManager() config = self._makeOne() subscriber = self._registerEventListener(config, IApplicationCreated) config.manager = manager app = config.make_wsgi_app() self.assertEqual(app.__class__, Router) self.assertEqual(manager.pushed['registry'], config.registry) self.assertEqual(manager.pushed['request'], None) self.assertTrue(manager.popped) self.assertEqual(pyramid.config.global_registries.last, app.registry) self.assertEqual(len(subscriber), 1) self.assertTrue(IApplicationCreated.providedBy(subscriber[0])) pyramid.config.global_registries.empty() def test_include_with_dotted_name(self): from pyramid.tests import test_config config = self._makeOne() config.include('pyramid.tests.test_config.dummy_include') after = config.action_state actions = after.actions self.assertEqual(len(actions), 1) action = after.actions[0] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_include_with_python_callable(self): from pyramid.tests import test_config config = self._makeOne() config.include(dummy_include) after = config.action_state actions = after.actions self.assertEqual(len(actions), 1) action = actions[0] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_include_with_module_defaults_to_includeme(self): from pyramid.tests import test_config config = self._makeOne() config.include('pyramid.tests.test_config') after = config.action_state actions = after.actions self.assertEqual(len(actions), 1) action = actions[0] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_include_with_module_defaults_to_includeme_missing(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.include, 'pyramid.tests') def test_include_with_route_prefix(self): root_config = self._makeOne(autocommit=True) def dummy_subapp(config): self.assertEqual(config.route_prefix, 'root') root_config.include(dummy_subapp, route_prefix='root') def test_include_with_nested_route_prefix(self): root_config = self._makeOne(autocommit=True, route_prefix='root') def dummy_subapp2(config): self.assertEqual(config.route_prefix, 'root/nested') def dummy_subapp3(config): self.assertEqual(config.route_prefix, 'root/nested/nested2') config.include(dummy_subapp4) def dummy_subapp4(config): self.assertEqual(config.route_prefix, 'root/nested/nested2') def dummy_subapp(config): self.assertEqual(config.route_prefix, 'root/nested') config.include(dummy_subapp2) config.include(dummy_subapp3, route_prefix='nested2') root_config.include(dummy_subapp, route_prefix='nested') def test_include_with_missing_source_file(self): from pyramid.exceptions import ConfigurationError import inspect config = self._makeOne() class DummyInspect(object): def getmodule(self, c): return inspect.getmodule(c) def getsourcefile(self, c): return None config.inspect = DummyInspect() try: config.include('pyramid.tests.test_config.dummy_include') except ConfigurationError as e: self.assertEqual( e.args[0], "No source file for module 'pyramid.tests.test_config' (.py " "file must exist, refusing to use orphan .pyc or .pyo file).") else: # pragma: no cover raise AssertionError def test_action_branching_kw_is_None(self): config = self._makeOne(autocommit=True) self.assertEqual(config.action('discrim'), None) def test_action_branching_kw_is_not_None(self): config = self._makeOne(autocommit=True) self.assertEqual(config.action('discrim', kw={'a':1}), None) def test_action_autocommit_with_introspectables(self): from pyramid.util import ActionInfo config = self._makeOne(autocommit=True) intr = DummyIntrospectable() config.action('discrim', introspectables=(intr,)) self.assertEqual(len(intr.registered), 1) self.assertEqual(intr.registered[0][0], config.introspector) self.assertEqual(intr.registered[0][1].__class__, ActionInfo) def test_action_autocommit_with_introspectables_introspection_off(self): config = self._makeOne(autocommit=True) config.introspection = False intr = DummyIntrospectable() config.action('discrim', introspectables=(intr,)) self.assertEqual(len(intr.registered), 0) def test_action_branching_nonautocommit_with_config_info(self): config = self._makeOne(autocommit=False) config.info = 'abc' state = DummyActionState() state.autocommit = False config.action_state = state config.action('discrim', kw={'a':1}) self.assertEqual( state.actions, [((), {'args': (), 'callable': None, 'discriminator': 'discrim', 'includepath': (), 'info': 'abc', 'introspectables': (), 'kw': {'a': 1}, 'order': 0})]) def test_action_branching_nonautocommit_without_config_info(self): config = self._makeOne(autocommit=False) config.info = '' config._ainfo = ['z'] state = DummyActionState() config.action_state = state state.autocommit = False config.action('discrim', kw={'a':1}) self.assertEqual( state.actions, [((), {'args': (), 'callable': None, 'discriminator': 'discrim', 'includepath': (), 'info': 'z', 'introspectables': (), 'kw': {'a': 1}, 'order': 0})]) def test_action_branching_nonautocommit_with_introspectables(self): config = self._makeOne(autocommit=False) config.info = '' config._ainfo = [] state = DummyActionState() config.action_state = state state.autocommit = False intr = DummyIntrospectable() config.action('discrim', introspectables=(intr,)) self.assertEqual( state.actions[0][1]['introspectables'], (intr,)) def test_action_nonautocommit_with_introspectables_introspection_off(self): config = self._makeOne(autocommit=False) config.info = '' config._ainfo = [] config.introspection = False state = DummyActionState() config.action_state = state state.autocommit = False intr = DummyIntrospectable() config.action('discrim', introspectables=(intr,)) self.assertEqual( state.actions[0][1]['introspectables'], ()) def test_scan_integration(self): from zope.interface import alsoProvides from pyramid.interfaces import IRequest from pyramid.view import render_view_to_response import pyramid.tests.test_config.pkgs.scannable as package config = self._makeOne(autocommit=True) config.scan(package) ctx = DummyContext() req = DummyRequest() alsoProvides(req, IRequest) req.registry = config.registry req.method = 'GET' result = render_view_to_response(ctx, req, '') self.assertEqual(result, 'grokked') req.method = 'POST' result = render_view_to_response(ctx, req, '') self.assertEqual(result, 'grokked_post') result= render_view_to_response(ctx, req, 'grokked_class') self.assertEqual(result, 'grokked_class') result= render_view_to_response(ctx, req, 'grokked_instance') self.assertEqual(result, 'grokked_instance') result= render_view_to_response(ctx, req, 'oldstyle_grokked_class') self.assertEqual(result, 'oldstyle_grokked_class') req.method = 'GET' result = render_view_to_response(ctx, req, 'another') self.assertEqual(result, 'another_grokked') req.method = 'POST' result = render_view_to_response(ctx, req, 'another') self.assertEqual(result, 'another_grokked_post') result= render_view_to_response(ctx, req, 'another_grokked_class') self.assertEqual(result, 'another_grokked_class') result= render_view_to_response(ctx, req, 'another_grokked_instance') self.assertEqual(result, 'another_grokked_instance') result= render_view_to_response(ctx, req, 'another_oldstyle_grokked_class') self.assertEqual(result, 'another_oldstyle_grokked_class') result = render_view_to_response(ctx, req, 'stacked1') self.assertEqual(result, 'stacked') result = render_view_to_response(ctx, req, 'stacked2') self.assertEqual(result, 'stacked') result = render_view_to_response(ctx, req, 'another_stacked1') self.assertEqual(result, 'another_stacked') result = render_view_to_response(ctx, req, 'another_stacked2') self.assertEqual(result, 'another_stacked') result = render_view_to_response(ctx, req, 'stacked_class1') self.assertEqual(result, 'stacked_class') result = render_view_to_response(ctx, req, 'stacked_class2') self.assertEqual(result, 'stacked_class') result = render_view_to_response(ctx, req, 'another_stacked_class1') self.assertEqual(result, 'another_stacked_class') result = render_view_to_response(ctx, req, 'another_stacked_class2') self.assertEqual(result, 'another_stacked_class') # NB: on Jython, a class without an __init__ apparently accepts # any number of arguments without raising a TypeError, so the next # assertion may fail there. We don't support Jython at the moment, # this is just a note to a future self. self.assertRaises(TypeError, render_view_to_response, ctx, req, 'basemethod') result = render_view_to_response(ctx, req, 'method1') self.assertEqual(result, 'method1') result = render_view_to_response(ctx, req, 'method2') self.assertEqual(result, 'method2') result = render_view_to_response(ctx, req, 'stacked_method1') self.assertEqual(result, 'stacked_method') result = render_view_to_response(ctx, req, 'stacked_method2') self.assertEqual(result, 'stacked_method') result = render_view_to_response(ctx, req, 'subpackage_init') self.assertEqual(result, 'subpackage_init') result = render_view_to_response(ctx, req, 'subpackage_notinit') self.assertEqual(result, 'subpackage_notinit') result = render_view_to_response(ctx, req, 'subsubpackage_init') self.assertEqual(result, 'subsubpackage_init') result = render_view_to_response(ctx, req, 'pod_notinit') self.assertEqual(result, None) def test_scan_integration_with_ignore(self): from zope.interface import alsoProvides from pyramid.interfaces import IRequest from pyramid.view import render_view_to_response import pyramid.tests.test_config.pkgs.scannable as package config = self._makeOne(autocommit=True) config.scan(package, ignore='pyramid.tests.test_config.pkgs.scannable.another') ctx = DummyContext() req = DummyRequest() alsoProvides(req, IRequest) req.registry = config.registry req.method = 'GET' result = render_view_to_response(ctx, req, '') self.assertEqual(result, 'grokked') # ignored v = render_view_to_response(ctx, req, 'another_stacked_class2') self.assertEqual(v, None) def test_scan_integration_dottedname_package(self): from zope.interface import alsoProvides from pyramid.interfaces import IRequest from pyramid.view import render_view_to_response config = self._makeOne(autocommit=True) config.scan('pyramid.tests.test_config.pkgs.scannable') ctx = DummyContext() req = DummyRequest() alsoProvides(req, IRequest) req.registry = config.registry req.method = 'GET' result = render_view_to_response(ctx, req, '') self.assertEqual(result, 'grokked') def test_scan_integration_with_extra_kw(self): config = self._makeOne(autocommit=True) config.scan('pyramid.tests.test_config.pkgs.scanextrakw', a=1) self.assertEqual(config.a, 1) def test_scan_integration_with_onerror(self): # fancy sys.path manipulation here to appease "setup.py test" which # fails miserably when it can't import something in the package import sys try: here = os.path.dirname(__file__) path = os.path.join(here, 'path') sys.path.append(path) config = self._makeOne(autocommit=True) class FooException(Exception): pass def onerror(name): raise FooException self.assertRaises(FooException, config.scan, 'scanerror', onerror=onerror) finally: sys.path.remove(path) def test_scan_integration_conflict(self): from pyramid.tests.test_config.pkgs import selfscan from pyramid.config import Configurator c = Configurator() c.scan(selfscan) c.scan(selfscan) try: c.commit() except ConfigurationConflictError as why: def scanconflicts(e): conflicts = e._conflicts.values() for conflict in conflicts: for confinst in conflict: yield confinst.src which = list(scanconflicts(why)) self.assertEqual(len(which), 4) self.assertTrue("@view_config(renderer='string')" in which) self.assertTrue("@view_config(name='two', renderer='string')" in which) @skip_on('py3') def test_hook_zca(self): from zope.component import getSiteManager def foo(): '123' try: config = self._makeOne() config.hook_zca() config.begin() sm = getSiteManager() self.assertEqual(sm, config.registry) finally: getSiteManager.reset() @skip_on('py3') def test_unhook_zca(self): from zope.component import getSiteManager def foo(): '123' try: getSiteManager.sethook(foo) config = self._makeOne() config.unhook_zca() sm = getSiteManager() self.assertNotEqual(sm, '123') finally: getSiteManager.reset() def test_commit_conflict_simple(self): config = self._makeOne() def view1(request): pass def view2(request): pass config.add_view(view1) config.add_view(view2) self.assertRaises(ConfigurationConflictError, config.commit) def test_commit_conflict_resolved_with_include(self): config = self._makeOne() def view1(request): pass def view2(request): pass def includeme(config): config.add_view(view2) config.add_view(view1) config.include(includeme) config.commit() registeredview = self._getViewCallable(config) self.assertEqual(registeredview.__name__, 'view1') def test_commit_conflict_with_two_includes(self): config = self._makeOne() def view1(request): pass def view2(request): pass def includeme1(config): config.add_view(view1) def includeme2(config): config.add_view(view2) config.include(includeme1) config.include(includeme2) try: config.commit() except ConfigurationConflictError as why: c1, c2 = _conflictFunctions(why) self.assertEqual(c1, 'includeme1') self.assertEqual(c2, 'includeme2') else: #pragma: no cover raise AssertionError def test_commit_conflict_resolved_with_two_includes_and_local(self): config = self._makeOne() def view1(request): pass def view2(request): pass def view3(request): pass def includeme1(config): config.add_view(view1) def includeme2(config): config.add_view(view2) config.include(includeme1) config.include(includeme2) config.add_view(view3) config.commit() registeredview = self._getViewCallable(config) self.assertEqual(registeredview.__name__, 'view3') def test_autocommit_no_conflicts(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) def view1(request): pass def view2(request): pass def view3(request): pass config.add_view(view1, renderer=null_renderer) config.add_view(view2, renderer=null_renderer) config.add_view(view3, renderer=null_renderer) config.commit() registeredview = self._getViewCallable(config) self.assertEqual(registeredview.__name__, 'view3') def test_conflict_set_notfound_view(self): config = self._makeOne() def view1(request): pass def view2(request): pass config.set_notfound_view(view1) config.set_notfound_view(view2) try: config.commit() except ConfigurationConflictError as why: c1, c2 = _conflictFunctions(why) self.assertEqual(c1, 'test_conflict_set_notfound_view') self.assertEqual(c2, 'test_conflict_set_notfound_view') else: # pragma: no cover raise AssertionError def test_conflict_set_forbidden_view(self): config = self._makeOne() def view1(request): pass def view2(request): pass config.set_forbidden_view(view1) config.set_forbidden_view(view2) try: config.commit() except ConfigurationConflictError as why: c1, c2 = _conflictFunctions(why) self.assertEqual(c1, 'test_conflict_set_forbidden_view') self.assertEqual(c2, 'test_conflict_set_forbidden_view') else: # pragma: no cover raise AssertionError def test___getattr__missing_when_directives_exist(self): config = self._makeOne() directives = {} config.registry._directives = directives self.assertRaises(AttributeError, config.__getattr__, 'wontexist') def test___getattr__missing_when_directives_dont_exist(self): config = self._makeOne() self.assertRaises(AttributeError, config.__getattr__, 'wontexist') def test___getattr__matches(self): config = self._makeOne() def foo(config): pass directives = {'foo':(foo, True)} config.registry._directives = directives foo_meth = config.foo self.assertTrue(getattr(foo_meth, im_func).__docobj__ is foo) def test___getattr__matches_no_action_wrap(self): config = self._makeOne() def foo(config): pass directives = {'foo':(foo, False)} config.registry._directives = directives foo_meth = config.foo self.assertTrue(getattr(foo_meth, im_func) is foo) class TestConfiguratorDeprecatedFeatures(unittest.TestCase): def setUp(self): self.warnings = warnings.catch_warnings() self.warnings.__enter__() warnings.filterwarnings('ignore') def tearDown(self): self.warnings.__exit__(None, None, None) def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) config.registry._dont_resolve_responses = True return config def _getRouteRequestIface(self, config, name): from pyramid.interfaces import IRouteRequest iface = config.registry.getUtility(IRouteRequest, name) return iface def _getViewCallable(self, config, ctx_iface=None, request_iface=None, name='', exception_view=False): from zope.interface import Interface from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier if exception_view: classifier = IExceptionViewClassifier else: classifier = IViewClassifier if ctx_iface is None: ctx_iface = Interface return config.registry.adapters.lookup( (classifier, request_iface, ctx_iface), IView, name=name, default=None) def _registerRenderer(self, config, name='.txt'): from pyramid.interfaces import IRendererFactory from pyramid.interfaces import ITemplateRenderer from zope.interface import implementer @implementer(ITemplateRenderer) class Renderer: def __init__(self, info): self.__class__.info = info def __call__(self, *arg): return 'Hello!' config.registry.registerUtility(Renderer, IRendererFactory, name=name) return Renderer def _assertRoute(self, config, name, path, num_predicates=0): from pyramid.interfaces import IRoutesMapper mapper = config.registry.getUtility(IRoutesMapper) routes = mapper.get_routes() route = routes[0] self.assertEqual(len(routes), 1) self.assertEqual(route.name, name) self.assertEqual(route.path, path) self.assertEqual(len(routes[0].predicates), num_predicates) return route def _makeRequest(self, config): request = DummyRequest() request.registry = config.registry return request def test_add_route_with_view(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, view_renderer=null_renderer) request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self.assertEqual(wrapper(None, None), 'OK') self._assertRoute(config, 'name', 'path') def test_add_route_with_view_context(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, view_context=IDummy, view_renderer=null_renderer) request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, IDummy, request_type) self.assertEqual(wrapper(None, None), 'OK') self._assertRoute(config, 'name', 'path') wrapper = self._getViewCallable(config, IOther, request_type) self.assertEqual(wrapper, None) def test_add_route_with_view_exception(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy config = self._makeOne(autocommit=True) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, view_context=RuntimeError, view_renderer=null_renderer) request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), request_iface=request_type, exception_view=True) self.assertEqual(wrapper(None, None), 'OK') self._assertRoute(config, 'name', 'path') wrapper = self._getViewCallable( config, ctx_iface=IOther, request_iface=request_type, exception_view=True) self.assertEqual(wrapper, None) def test_add_route_with_view_for(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, view_for=IDummy, view_renderer=null_renderer) request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, IDummy, request_type) self.assertEqual(wrapper(None, None), 'OK') self._assertRoute(config, 'name', 'path') wrapper = self._getViewCallable(config, IOther, request_type) self.assertEqual(wrapper, None) def test_add_route_with_for_(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, for_=IDummy, view_renderer=null_renderer) request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, IDummy, request_type) self.assertEqual(wrapper(None, None), 'OK') self._assertRoute(config, 'name', 'path') wrapper = self._getViewCallable(config, IOther, request_type) self.assertEqual(wrapper, None) def test_add_route_with_view_renderer(self): config = self._makeOne(autocommit=True) self._registerRenderer(config) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, view_renderer='files/minimal.txt') request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self._assertRoute(config, 'name', 'path') self.assertEqual(wrapper(None, None).body, b'Hello!') def test_add_route_with_view_attr(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) self._registerRenderer(config) class View(object): def __init__(self, context, request): pass def alt(self): return 'OK' config.add_route('name', 'path', view=View, view_attr='alt', view_renderer=null_renderer) request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self._assertRoute(config, 'name', 'path') request = self._makeRequest(config) self.assertEqual(wrapper(None, request), 'OK') def test_add_route_with_view_renderer_alias(self): config = self._makeOne(autocommit=True) self._registerRenderer(config) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, renderer='files/minimal.txt') request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self._assertRoute(config, 'name', 'path') self.assertEqual(wrapper(None, None).body, b'Hello!') def test_add_route_with_view_permission(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy config = self._makeOne(autocommit=True) policy = lambda *arg: None config.registry.registerUtility(policy, IAuthenticationPolicy) config.registry.registerUtility(policy, IAuthorizationPolicy) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, view_permission='edit') request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self._assertRoute(config, 'name', 'path') self.assertTrue(hasattr(wrapper, '__call_permissive__')) def test_add_route_with_view_permission_alias(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy config = self._makeOne(autocommit=True) policy = lambda *arg: None config.registry.registerUtility(policy, IAuthenticationPolicy) config.registry.registerUtility(policy, IAuthorizationPolicy) view = lambda *arg: 'OK' config.add_route('name', 'path', view=view, permission='edit') request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self._assertRoute(config, 'name', 'path') self.assertTrue(hasattr(wrapper, '__call_permissive__')) def test_conflict_route_with_view(self): config = self._makeOne() def view1(request): pass def view2(request): pass config.add_route('a', '/a', view=view1) config.add_route('a', '/a', view=view2) try: config.commit() except ConfigurationConflictError as why: c1, c2 = _conflictFunctions(why) self.assertEqual(c1, 'test_conflict_route_with_view') self.assertEqual(c2, 'test_conflict_route_with_view') else: # pragma: no cover raise AssertionError class TestConfigurator_add_directive(unittest.TestCase): def setUp(self): from pyramid.config import Configurator self.config = Configurator() def test_extend_with_dotted_name(self): from pyramid.tests import test_config config = self.config config.add_directive( 'dummy_extend', 'pyramid.tests.test_config.dummy_extend') self.assertTrue(hasattr(config, 'dummy_extend')) config.dummy_extend('discrim') after = config.action_state action = after.actions[-1] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_add_directive_with_partial(self): from pyramid.tests import test_config config = self.config config.add_directive( 'dummy_partial', 'pyramid.tests.test_config.dummy_partial') self.assertTrue(hasattr(config, 'dummy_partial')) config.dummy_partial() after = config.action_state action = after.actions[-1] self.assertEqual(action['discriminator'], 'partial') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_add_directive_with_custom_callable(self): from pyramid.tests import test_config config = self.config config.add_directive( 'dummy_callable', 'pyramid.tests.test_config.dummy_callable') self.assertTrue(hasattr(config, 'dummy_callable')) config.dummy_callable('discrim') after = config.action_state action = after.actions[-1] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_extend_with_python_callable(self): from pyramid.tests import test_config config = self.config config.add_directive( 'dummy_extend', dummy_extend) self.assertTrue(hasattr(config, 'dummy_extend')) config.dummy_extend('discrim') after = config.action_state action = after.actions[-1] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) def test_extend_same_name_doesnt_conflict(self): config = self.config config.add_directive( 'dummy_extend', dummy_extend) config.add_directive( 'dummy_extend', dummy_extend2) self.assertTrue(hasattr(config, 'dummy_extend')) config.dummy_extend('discrim') after = config.action_state action = after.actions[-1] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], config.registry) def test_extend_action_method_successful(self): config = self.config config.add_directive( 'dummy_extend', dummy_extend) config.dummy_extend('discrim') config.dummy_extend('discrim') self.assertRaises(ConfigurationConflictError, config.commit) def test_directive_persists_across_configurator_creations(self): config = self.config config.add_directive('dummy_extend', dummy_extend) config2 = config.with_package('pyramid.tests') config2.dummy_extend('discrim') after = config2.action_state actions = after.actions self.assertEqual(len(actions), 1) action = actions[0] self.assertEqual(action['discriminator'], 'discrim') self.assertEqual(action['callable'], None) self.assertEqual(action['args'], config2.package) class TestActionState(unittest.TestCase): def _makeOne(self): from pyramid.config import ActionState return ActionState() def test_it(self): c = self._makeOne() self.assertEqual(c.actions, []) def test_action_simple(self): from pyramid.tests.test_config import dummyfactory as f c = self._makeOne() c.actions = [] c.action(1, f, (1,), {'x':1}) self.assertEqual( c.actions, [{'args': (1,), 'callable': f, 'discriminator': 1, 'includepath': (), 'info': None, 'introspectables': (), 'kw': {'x': 1}, 'order': 0}]) c.action(None) self.assertEqual( c.actions, [{'args': (1,), 'callable': f, 'discriminator': 1, 'includepath': (), 'info': None, 'introspectables': (), 'kw': {'x': 1}, 'order': 0}, {'args': (), 'callable': None, 'discriminator': None, 'includepath': (), 'info': None, 'introspectables': (), 'kw': {}, 'order': 0},]) def test_action_with_includepath(self): c = self._makeOne() c.actions = [] c.action(None, includepath=('abc',)) self.assertEqual( c.actions, [{'args': (), 'callable': None, 'discriminator': None, 'includepath': ('abc',), 'info': None, 'introspectables': (), 'kw': {}, 'order': 0}]) def test_action_with_info(self): c = self._makeOne() c.action(None, info='abc') self.assertEqual( c.actions, [{'args': (), 'callable': None, 'discriminator': None, 'includepath': (), 'info': 'abc', 'introspectables': (), 'kw': {}, 'order': 0}]) def test_action_with_includepath_and_info(self): c = self._makeOne() c.action(None, includepath=('spec',), info='bleh') self.assertEqual( c.actions, [{'args': (), 'callable': None, 'discriminator': None, 'includepath': ('spec',), 'info': 'bleh', 'introspectables': (), 'kw': {}, 'order': 0}]) def test_action_with_order(self): c = self._makeOne() c.actions = [] c.action(None, order=99999) self.assertEqual( c.actions, [{'args': (), 'callable': None, 'discriminator': None, 'includepath': (), 'info': None, 'introspectables': (), 'kw': {}, 'order': 99999}]) def test_action_with_introspectables(self): c = self._makeOne() c.actions = [] intr = DummyIntrospectable() c.action(None, introspectables=(intr,)) self.assertEqual( c.actions, [{'args': (), 'callable': None, 'discriminator': None, 'includepath': (), 'info': None, 'introspectables': (intr,), 'kw': {}, 'order': 0}]) def test_processSpec(self): c = self._makeOne() self.assertTrue(c.processSpec('spec')) self.assertFalse(c.processSpec('spec')) def test_execute_actions_tuples(self): output = [] def f(*a, **k): output.append((a, k)) c = self._makeOne() c.actions = [ (1, f, (1,)), (1, f, (11,), {}, ('x', )), (2, f, (2,)), (None, None), ] c.execute_actions() self.assertEqual(output, [((1,), {}), ((2,), {})]) def test_execute_actions_dicts(self): output = [] def f(*a, **k): output.append((a, k)) c = self._makeOne() c.actions = [ {'discriminator':1, 'callable':f, 'args':(1,), 'kw':{}, 'order':0, 'includepath':(), 'info':None, 'introspectables':()}, {'discriminator':1, 'callable':f, 'args':(11,), 'kw':{}, 'includepath':('x',), 'order': 0, 'info':None, 'introspectables':()}, {'discriminator':2, 'callable':f, 'args':(2,), 'kw':{}, 'order':0, 'includepath':(), 'info':None, 'introspectables':()}, {'discriminator':None, 'callable':None, 'args':(), 'kw':{}, 'order':0, 'includepath':(), 'info':None, 'introspectables':()}, ] c.execute_actions() self.assertEqual(output, [((1,), {}), ((2,), {})]) def test_execute_actions_with_introspectables(self): output = [] def f(*a, **k): output.append((a, k)) c = self._makeOne() intr = DummyIntrospectable() c.actions = [ {'discriminator':1, 'callable':f, 'args':(1,), 'kw':{}, 'order':0, 'includepath':(), 'info':None, 'introspectables':(intr,)}, ] introspector = object() c.execute_actions(introspector=introspector) self.assertEqual(output, [((1,), {})]) self.assertEqual(intr.registered, [(introspector, None)]) def test_execute_actions_with_introspectable_no_callable(self): c = self._makeOne() intr = DummyIntrospectable() c.actions = [ {'discriminator':1, 'callable':None, 'args':(1,), 'kw':{}, 'order':0, 'includepath':(), 'info':None, 'introspectables':(intr,)}, ] introspector = object() c.execute_actions(introspector=introspector) self.assertEqual(intr.registered, [(introspector, None)]) def test_execute_actions_error(self): output = [] def f(*a, **k): output.append(('f', a, k)) def bad(): raise NotImplementedError c = self._makeOne() c.actions = [ (1, f, (1,)), (1, f, (11,), {}, ('x', )), (2, f, (2,)), (3, bad, (), {}, (), 'oops') ] self.assertRaises(ConfigurationExecutionError, c.execute_actions) self.assertEqual(output, [('f', (1,), {}), ('f', (2,), {})]) class Test_resolveConflicts(unittest.TestCase): def _callFUT(self, actions): from pyramid.config import resolveConflicts return resolveConflicts(actions) def test_it_success_tuples(self): from pyramid.tests.test_config import dummyfactory as f result = self._callFUT([ (None, f), (1, f, (1,), {}, (), 'first'), (1, f, (2,), {}, ('x',), 'second'), (1, f, (3,), {}, ('y',), 'third'), (4, f, (4,), {}, ('y',), 'should be last', 99999), (3, f, (3,), {}, ('y',)), (None, f, (5,), {}, ('y',)), ]) result = list(result) self.assertEqual( result, [{'info': None, 'args': (), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': None, 'includepath': (), 'order': 0}, {'info': 'first', 'args': (1,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 1, 'includepath': (), 'order': 0}, {'info': None, 'args': (3,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 3, 'includepath': ('y',), 'order': 0}, {'info': None, 'args': (5,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': None, 'includepath': ('y',), 'order': 0}, {'info': 'should be last', 'args': (4,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 4, 'includepath': ('y',), 'order': 99999} ] ) def test_it_success_dicts(self): from pyramid.tests.test_config import dummyfactory as f from pyramid.config import expand_action result = self._callFUT([ expand_action(None, f), expand_action(1, f, (1,), {}, (), 'first'), expand_action(1, f, (2,), {}, ('x',), 'second'), expand_action(1, f, (3,), {}, ('y',), 'third'), expand_action(4, f, (4,), {}, ('y',), 'should be last', 99999), expand_action(3, f, (3,), {}, ('y',)), expand_action(None, f, (5,), {}, ('y',)), ]) result = list(result) self.assertEqual( result, [{'info': None, 'args': (), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': None, 'includepath': (), 'order': 0}, {'info': 'first', 'args': (1,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 1, 'includepath': (), 'order': 0}, {'info': None, 'args': (3,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 3, 'includepath': ('y',), 'order': 0}, {'info': None, 'args': (5,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': None, 'includepath': ('y',), 'order': 0}, {'info': 'should be last', 'args': (4,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 4, 'includepath': ('y',), 'order': 99999} ] ) def test_it_conflict(self): from pyramid.tests.test_config import dummyfactory as f result = self._callFUT([ (None, f), (1, f, (2,), {}, ('x',), 'eek'), # will conflict (1, f, (3,), {}, ('y',), 'ack'), # will conflict (4, f, (4,), {}, ('y',)), (3, f, (3,), {}, ('y',)), (None, f, (5,), {}, ('y',)), ]) self.assertRaises(ConfigurationConflictError, list, result) def test_it_with_actions_grouped_by_order(self): from pyramid.tests.test_config import dummyfactory as f from pyramid.config import expand_action result = self._callFUT([ expand_action(None, f), # X expand_action(1, f, (1,), {}, (), 'third', 10), # X expand_action(1, f, (2,), {}, ('x',), 'fourth', 10), expand_action(1, f, (3,), {}, ('y',), 'fifth', 10), expand_action(2, f, (1,), {}, (), 'sixth', 10), # X expand_action(3, f, (1,), {}, (), 'seventh', 10), # X expand_action(5, f, (4,), {}, ('y',), 'eighth', 99999), # X expand_action(4, f, (3,), {}, (), 'first', 5), # X expand_action(4, f, (5,), {}, ('y',), 'second', 5), ]) result = list(result) self.assertEqual(len(result), 6) # resolved actions should be grouped by (order, i) self.assertEqual( result, [{'info': None, 'args': (), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': None, 'includepath': (), 'order': 0}, {'info': 'first', 'args': (3,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 4, 'includepath': (), 'order': 5}, {'info': 'third', 'args': (1,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 1, 'includepath': (), 'order': 10}, {'info': 'sixth', 'args': (1,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 2, 'includepath': (), 'order': 10}, {'info': 'seventh', 'args': (1,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 3, 'includepath': (), 'order': 10}, {'info': 'eighth', 'args': (4,), 'callable': f, 'introspectables': (), 'kw': {}, 'discriminator': 5, 'includepath': ('y',), 'order': 99999} ] ) class TestGlobalRegistriesIntegration(unittest.TestCase): def setUp(self): from pyramid.config import global_registries global_registries.empty() tearDown = setUp def _makeConfigurator(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_global_registries_empty(self): from pyramid.config import global_registries self.assertEqual(global_registries.last, None) def test_global_registries(self): from pyramid.config import global_registries config1 = self._makeConfigurator() config1.make_wsgi_app() self.assertEqual(global_registries.last, config1.registry) config2 = self._makeConfigurator() config2.make_wsgi_app() self.assertEqual(global_registries.last, config2.registry) self.assertEqual(list(global_registries), [config1.registry, config2.registry]) global_registries.remove(config2.registry) self.assertEqual(global_registries.last, config1.registry) class DummyRequest: subpath = () matchdict = None def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ self.params = {} self.cookies = {} class DummyResponse: status = '200 OK' headerlist = () app_iter = () body = '' class DummyThreadLocalManager(object): pushed = None popped = False def push(self, d): self.pushed = d def pop(self): self.popped = True from zope.interface import implementer @implementer(IDummy) class DummyEvent: pass class DummyRegistry(object): def __init__(self, adaptation=None, util=None): self.utilities = [] self.adapters = [] self.adaptation = adaptation self.util = util def subscribers(self, events, name): self.events = events return events def registerUtility(self, *arg, **kw): self.utilities.append((arg, kw)) def registerAdapter(self, *arg, **kw): self.adapters.append((arg, kw)) def queryAdapter(self, *arg, **kw): return self.adaptation def queryUtility(self, *arg, **kw): return self.util from pyramid.interfaces import IResponse @implementer(IResponse) class DummyResponse(object): pass from zope.interface import Interface class IOther(Interface): pass def _conflictFunctions(e): conflicts = e._conflicts.values() for conflict in conflicts: for confinst in conflict: yield confinst.function class DummyActionState(object): autocommit = False info = '' def __init__(self): self.actions = [] def action(self, *arg, **kw): self.actions.append((arg, kw)) class DummyIntrospectable(object): def __init__(self): self.registered = [] def register(self, introspector, action_info): self.registered.append((introspector, action_info)) pyramid-1.4.5/pyramid/tests/test_config/test_routes.py0000664000175000017500000002370012203712502022523 0ustar takakitakakiimport unittest from pyramid.tests.test_config import dummyfactory from pyramid.tests.test_config import DummyContext from pyramid.compat import text_ class RoutesConfiguratorMixinTests(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def _assertRoute(self, config, name, path, num_predicates=0): from pyramid.interfaces import IRoutesMapper mapper = config.registry.getUtility(IRoutesMapper) routes = mapper.get_routes() route = routes[0] self.assertEqual(len(routes), 1) self.assertEqual(route.name, name) self.assertEqual(route.path, path) self.assertEqual(len(routes[0].predicates), num_predicates) return route def _makeRequest(self, config): request = DummyRequest() request.registry = config.registry return request def test_get_routes_mapper_not_yet_registered(self): config = self._makeOne() mapper = config.get_routes_mapper() self.assertEqual(mapper.routelist, []) def test_get_routes_mapper_already_registered(self): from pyramid.interfaces import IRoutesMapper config = self._makeOne() mapper = object() config.registry.registerUtility(mapper, IRoutesMapper) result = config.get_routes_mapper() self.assertEqual(result, mapper) def test_add_route_defaults(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path') self._assertRoute(config, 'name', 'path') def test_add_route_with_route_prefix(self): config = self._makeOne(autocommit=True) config.route_prefix = 'root' config.add_route('name', 'path') self._assertRoute(config, 'name', 'root/path') def test_add_route_discriminator(self): config = self._makeOne() config.add_route('name', 'path') self.assertEqual(config.action_state.actions[-1]['discriminator'], ('route', 'name')) def test_add_route_with_factory(self): config = self._makeOne(autocommit=True) factory = object() config.add_route('name', 'path', factory=factory) route = self._assertRoute(config, 'name', 'path') self.assertEqual(route.factory, factory) def test_add_route_with_static(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path/{foo}', static=True) mapper = config.get_routes_mapper() self.assertEqual(len(mapper.get_routes()), 0) self.assertEqual(mapper.generate('name', {"foo":"a"}), '/path/a') def test_add_route_with_factory_dottedname(self): config = self._makeOne(autocommit=True) config.add_route( 'name', 'path', factory='pyramid.tests.test_config.dummyfactory') route = self._assertRoute(config, 'name', 'path') self.assertEqual(route.factory, dummyfactory) def test_add_route_with_xhr(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', xhr=True) route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.is_xhr = True self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.is_xhr = False self.assertEqual(predicate(None, request), False) def test_add_route_with_request_method(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', request_method='GET') route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.method = 'GET' self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.method = 'POST' self.assertEqual(predicate(None, request), False) def test_add_route_with_path_info(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', path_info='/foo') route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.upath_info = '/foo' self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.upath_info = '/' self.assertEqual(predicate(None, request), False) def test_add_route_with_path_info_highorder(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', path_info=text_(b'/La Pe\xc3\xb1a', 'utf-8')) route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.upath_info = text_(b'/La Pe\xc3\xb1a', 'utf-8') self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.upath_info = text_('/') self.assertEqual(predicate(None, request), False) def test_add_route_with_path_info_regex(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', path_info=text_(br'/La Pe\w*', 'utf-8')) route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.upath_info = text_(b'/La Pe\xc3\xb1a', 'utf-8') self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.upath_info = text_('/') self.assertEqual(predicate(None, request), False) def test_add_route_with_request_param(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', request_param='abc') route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.params = {'abc':'123'} self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.params = {} self.assertEqual(predicate(None, request), False) def test_add_route_with_custom_predicates(self): config = self._makeOne(autocommit=True) def pred1(context, request): pass def pred2(context, request): pass config.add_route('name', 'path', custom_predicates=(pred1, pred2)) route = self._assertRoute(config, 'name', 'path', 2) self.assertEqual(len(route.predicates), 2) def test_add_route_with_header(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', header='Host') route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.headers = {'Host':'example.com'} self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.headers = {} self.assertEqual(predicate(None, request), False) def test_add_route_with_accept(self): config = self._makeOne(autocommit=True) config.add_route('name', 'path', accept='text/xml') route = self._assertRoute(config, 'name', 'path', 1) predicate = route.predicates[0] request = self._makeRequest(config) request.accept = ['text/xml'] self.assertEqual(predicate(None, request), True) request = self._makeRequest(config) request.accept = ['text/html'] self.assertEqual(predicate(None, request), False) def test_add_route_no_pattern_with_path(self): config = self._makeOne(autocommit=True) config.add_route('name', path='path') self._assertRoute(config, 'name', 'path') def test_add_route_no_path_no_pattern(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.add_route, 'name') def test_add_route_with_pregenerator(self): config = self._makeOne(autocommit=True) config.add_route('name', 'pattern', pregenerator='123') route = self._assertRoute(config, 'name', 'pattern') self.assertEqual(route.pregenerator, '123') def test_add_route_no_view_with_view_attr(self): config = self._makeOne(autocommit=True) from pyramid.exceptions import ConfigurationError try: config.add_route('name', '/pattern', view_attr='abc') except ConfigurationError: pass else: # pragma: no cover raise AssertionError def test_add_route_no_view_with_view_context(self): config = self._makeOne(autocommit=True) from pyramid.exceptions import ConfigurationError try: config.add_route('name', '/pattern', view_context=DummyContext) except ConfigurationError: pass else: # pragma: no cover raise AssertionError def test_add_route_no_view_with_view_permission(self): config = self._makeOne(autocommit=True) from pyramid.exceptions import ConfigurationError try: config.add_route('name', '/pattern', view_permission='edit') except ConfigurationError: pass else: # pragma: no cover raise AssertionError def test_add_route_no_view_with_view_renderer(self): config = self._makeOne(autocommit=True) from pyramid.exceptions import ConfigurationError try: config.add_route('name', '/pattern', view_renderer='json') except ConfigurationError: pass else: # pragma: no cover raise AssertionError class DummyRequest: subpath = () matchdict = None def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ self.params = {} self.cookies = {} pyramid-1.4.5/pyramid/tests/test_config/test_predicates.py0000664000175000017500000004120112203712502023321 0ustar takakitakakiimport unittest from pyramid import testing from pyramid.compat import text_ class TestXHRPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import XHRPredicate return XHRPredicate(val, None) def test___call___true(self): inst = self._makeOne(True) request = Dummy() request.is_xhr = True result = inst(None, request) self.assertTrue(result) def test___call___false(self): inst = self._makeOne(True) request = Dummy() request.is_xhr = False result = inst(None, request) self.assertFalse(result) def test_text(self): inst = self._makeOne(True) self.assertEqual(inst.text(), 'xhr = True') def test_phash(self): inst = self._makeOne(True) self.assertEqual(inst.phash(), 'xhr = True') class TestRequestMethodPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import RequestMethodPredicate return RequestMethodPredicate(val, None) def test_ctor_get_but_no_head(self): inst = self._makeOne('GET') self.assertEqual(inst.val, ('GET', 'HEAD')) def test___call___true_single(self): inst = self._makeOne('GET') request = Dummy() request.method = 'GET' result = inst(None, request) self.assertTrue(result) def test___call___true_multi(self): inst = self._makeOne(('GET','HEAD')) request = Dummy() request.method = 'GET' result = inst(None, request) self.assertTrue(result) def test___call___false(self): inst = self._makeOne(('GET','HEAD')) request = Dummy() request.method = 'POST' result = inst(None, request) self.assertFalse(result) def test_text(self): inst = self._makeOne(('HEAD','GET')) self.assertEqual(inst.text(), 'request_method = GET,HEAD') def test_phash(self): inst = self._makeOne(('HEAD','GET')) self.assertEqual(inst.phash(), 'request_method = GET,HEAD') class TestPathInfoPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import PathInfoPredicate return PathInfoPredicate(val, None) def test_ctor_compilefail(self): from pyramid.exceptions import ConfigurationError self.assertRaises(ConfigurationError, self._makeOne, '\\') def test___call___true(self): inst = self._makeOne(r'/\d{2}') request = Dummy() request.upath_info = text_('/12') result = inst(None, request) self.assertTrue(result) def test___call___false(self): inst = self._makeOne(r'/\d{2}') request = Dummy() request.upath_info = text_('/n12') result = inst(None, request) self.assertFalse(result) def test_text(self): inst = self._makeOne('/') self.assertEqual(inst.text(), 'path_info = /') def test_phash(self): inst = self._makeOne('/') self.assertEqual(inst.phash(), 'path_info = /') class TestRequestParamPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import RequestParamPredicate return RequestParamPredicate(val, None) def test___call___true_exists(self): inst = self._makeOne('abc') request = Dummy() request.params = {'abc':1} result = inst(None, request) self.assertTrue(result) def test___call___true_withval(self): inst = self._makeOne('abc=1') request = Dummy() request.params = {'abc':'1'} result = inst(None, request) self.assertTrue(result) def test___call___true_multi(self): inst = self._makeOne(('abc', 'def =2 ')) request = Dummy() request.params = {'abc':'1', 'def': '2'} result = inst(None, request) self.assertTrue(result) def test___call___false_multi(self): inst = self._makeOne(('abc=3', 'def =2 ')) request = Dummy() request.params = {'abc':'3', 'def': '1'} result = inst(None, request) self.assertFalse(result) def test___call___false(self): inst = self._makeOne('abc') request = Dummy() request.params = {} result = inst(None, request) self.assertFalse(result) def test_text_exists(self): inst = self._makeOne('abc') self.assertEqual(inst.text(), 'request_param abc') def test_text_withval(self): inst = self._makeOne('abc= 1') self.assertEqual(inst.text(), 'request_param abc=1') def test_text_multi(self): inst = self._makeOne(('abc= 1', 'def')) self.assertEqual(inst.text(), 'request_param abc=1,def') def test_phash_exists(self): inst = self._makeOne('abc') self.assertEqual(inst.phash(), 'request_param abc') def test_phash_withval(self): inst = self._makeOne('abc= 1') self.assertEqual(inst.phash(), "request_param abc=1") class TestMatchParamPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import MatchParamPredicate return MatchParamPredicate(val, None) def test___call___true_single(self): inst = self._makeOne('abc=1') request = Dummy() request.matchdict = {'abc':'1'} result = inst(None, request) self.assertTrue(result) def test___call___true_multi(self): inst = self._makeOne(('abc=1', 'def=2')) request = Dummy() request.matchdict = {'abc':'1', 'def':'2'} result = inst(None, request) self.assertTrue(result) def test___call___false(self): inst = self._makeOne('abc=1') request = Dummy() request.matchdict = {} result = inst(None, request) self.assertFalse(result) def test___call___matchdict_is_None(self): inst = self._makeOne('abc=1') request = Dummy() request.matchdict = None result = inst(None, request) self.assertFalse(result) def test_text(self): inst = self._makeOne(('def= 1', 'abc =2')) self.assertEqual(inst.text(), 'match_param abc=2,def=1') def test_phash(self): inst = self._makeOne(('def= 1', 'abc =2')) self.assertEqual(inst.phash(), 'match_param abc=2,def=1') class TestCustomPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import CustomPredicate return CustomPredicate(val, None) def test___call___true(self): def func(context, request): self.assertEqual(context, None) self.assertEqual(request, None) return True inst = self._makeOne(func) result = inst(None, None) self.assertTrue(result) def test___call___false(self): def func(context, request): self.assertEqual(context, None) self.assertEqual(request, None) return False inst = self._makeOne(func) result = inst(None, None) self.assertFalse(result) def test_text_func_has___text__(self): pred = predicate() pred.__text__ = 'text' inst = self._makeOne(pred) self.assertEqual(inst.text(), 'text') def test_text_func_repr(self): pred = predicate() inst = self._makeOne(pred) self.assertEqual(inst.text(), 'custom predicate: object predicate') def test_phash(self): pred = predicate() inst = self._makeOne(pred) self.assertEqual(inst.phash(), 'custom:1') class TestTraversePredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import TraversePredicate return TraversePredicate(val, None) def test___call__traverse_has_remainder_already(self): inst = self._makeOne('/1/:a/:b') info = {'traverse':'abc'} request = Dummy() result = inst(info, request) self.assertEqual(result, True) self.assertEqual(info, {'traverse':'abc'}) def test___call__traverse_matches(self): inst = self._makeOne('/1/:a/:b') info = {'match':{'a':'a', 'b':'b'}} request = Dummy() result = inst(info, request) self.assertEqual(result, True) self.assertEqual(info, {'match': {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) def test___call__traverse_matches_with_highorder_chars(self): inst = self._makeOne(text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8')) info = {'match':{'x':text_(b'Qu\xc3\xa9bec', 'utf-8')}} request = Dummy() result = inst(info, request) self.assertEqual(result, True) self.assertEqual( info['match']['traverse'], (text_(b'La Pe\xc3\xb1a', 'utf-8'), text_(b'Qu\xc3\xa9bec', 'utf-8')) ) def test_text(self): inst = self._makeOne('/abc') self.assertEqual(inst.text(), 'traverse matchdict pseudo-predicate') def test_phash(self): inst = self._makeOne('/abc') self.assertEqual(inst.phash(), '') class Test_CheckCSRFTokenPredicate(unittest.TestCase): def _makeOne(self, val, config): from pyramid.config.predicates import CheckCSRFTokenPredicate return CheckCSRFTokenPredicate(val, config) def test_text(self): inst = self._makeOne(True, None) self.assertEqual(inst.text(), 'check_csrf = True') def test_phash(self): inst = self._makeOne(True, None) self.assertEqual(inst.phash(), 'check_csrf = True') def test_it_call_val_True(self): inst = self._makeOne(True, None) request = Dummy() def check_csrf_token(req, val, raises=True): self.assertEqual(req, request) self.assertEqual(val, 'csrf_token') self.assertEqual(raises, False) return True inst.check_csrf_token = check_csrf_token result = inst(None, request) self.assertEqual(result, True) def test_it_call_val_str(self): inst = self._makeOne('abc', None) request = Dummy() def check_csrf_token(req, val, raises=True): self.assertEqual(req, request) self.assertEqual(val, 'abc') self.assertEqual(raises, False) return True inst.check_csrf_token = check_csrf_token result = inst(None, request) self.assertEqual(result, True) def test_it_call_val_False(self): inst = self._makeOne(False, None) request = Dummy() result = inst(None, request) self.assertEqual(result, True) class TestHeaderPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import HeaderPredicate return HeaderPredicate(val, None) def test___call___true_exists(self): inst = self._makeOne('abc') request = Dummy() request.headers = {'abc':1} result = inst(None, request) self.assertTrue(result) def test___call___true_withval(self): inst = self._makeOne('abc:1') request = Dummy() request.headers = {'abc':'1'} result = inst(None, request) self.assertTrue(result) def test___call___true_withregex(self): inst = self._makeOne(r'abc:\d+') request = Dummy() request.headers = {'abc':'1'} result = inst(None, request) self.assertTrue(result) def test___call___false_withregex(self): inst = self._makeOne(r'abc:\d+') request = Dummy() request.headers = {'abc':'a'} result = inst(None, request) self.assertFalse(result) def test___call___false(self): inst = self._makeOne('abc') request = Dummy() request.headers = {} result = inst(None, request) self.assertFalse(result) def test_text_exists(self): inst = self._makeOne('abc') self.assertEqual(inst.text(), 'header abc') def test_text_withval(self): inst = self._makeOne('abc:1') self.assertEqual(inst.text(), 'header abc=1') def test_text_withregex(self): inst = self._makeOne(r'abc:\d+') self.assertEqual(inst.text(), r'header abc=\d+') def test_phash_exists(self): inst = self._makeOne('abc') self.assertEqual(inst.phash(), 'header abc') def test_phash_withval(self): inst = self._makeOne('abc:1') self.assertEqual(inst.phash(), "header abc=1") def test_phash_withregex(self): inst = self._makeOne(r'abc:\d+') self.assertEqual(inst.phash(), r'header abc=\d+') class Test_PhysicalPathPredicate(unittest.TestCase): def _makeOne(self, val, config): from pyramid.config.predicates import PhysicalPathPredicate return PhysicalPathPredicate(val, config) def test_text(self): inst = self._makeOne('/', None) self.assertEqual(inst.text(), "physical_path = ('',)") def test_phash(self): inst = self._makeOne('/', None) self.assertEqual(inst.phash(), "physical_path = ('',)") def test_it_call_val_tuple_True(self): inst = self._makeOne(('', 'abc'), None) root = Dummy() root.__name__ = '' root.__parent__ = None context = Dummy() context.__name__ = 'abc' context.__parent__ = root self.assertTrue(inst(context, None)) def test_it_call_val_list_True(self): inst = self._makeOne(['', 'abc'], None) root = Dummy() root.__name__ = '' root.__parent__ = None context = Dummy() context.__name__ = 'abc' context.__parent__ = root self.assertTrue(inst(context, None)) def test_it_call_val_str_True(self): inst = self._makeOne('/abc', None) root = Dummy() root.__name__ = '' root.__parent__ = None context = Dummy() context.__name__ = 'abc' context.__parent__ = root self.assertTrue(inst(context, None)) def test_it_call_False(self): inst = self._makeOne('/', None) root = Dummy() root.__name__ = '' root.__parent__ = None context = Dummy() context.__name__ = 'abc' context.__parent__ = root self.assertFalse(inst(context, None)) def test_it_call_context_has_no_name(self): inst = self._makeOne('/', None) context = Dummy() self.assertFalse(inst(context, None)) class Test_EffectivePrincipalsPredicate(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _makeOne(self, val, config): from pyramid.config.predicates import EffectivePrincipalsPredicate return EffectivePrincipalsPredicate(val, config) def test_text(self): inst = self._makeOne(('verna', 'fred'), None) self.assertEqual(inst.text(), "effective_principals = ['fred', 'verna']") def test_text_noniter(self): inst = self._makeOne('verna', None) self.assertEqual(inst.text(), "effective_principals = ['verna']") def test_phash(self): inst = self._makeOne(('verna', 'fred'), None) self.assertEqual(inst.phash(), "effective_principals = ['fred', 'verna']") def test_it_call_no_authentication_policy(self): request = testing.DummyRequest() inst = self._makeOne(('verna', 'fred'), None) context = Dummy() self.assertFalse(inst(context, request)) def test_it_call_authentication_policy_provides_superset(self): request = testing.DummyRequest() self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) inst = self._makeOne(('verna', 'fred'), None) context = Dummy() self.assertTrue(inst(context, request)) def test_it_call_authentication_policy_provides_superset_implicit(self): from pyramid.security import Authenticated request = testing.DummyRequest() self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) inst = self._makeOne(Authenticated, None) context = Dummy() self.assertTrue(inst(context, request)) def test_it_call_authentication_policy_doesnt_provide_superset(self): request = testing.DummyRequest() self.config.testing_securitypolicy('fred') inst = self._makeOne(('verna', 'fred'), None) context = Dummy() self.assertFalse(inst(context, request)) class predicate(object): def __repr__(self): return 'predicate' def __hash__(self): return 1 class Dummy(object): def __init__(self, **kw): self.__dict__.update(**kw) pyramid-1.4.5/pyramid/tests/test_config/pkgs/0000775000175000017500000000000012210157153020536 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/scanextrakw/0000775000175000017500000000000012210157153023070 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/scanextrakw/__init__.py0000664000175000017500000000033312203712502025175 0ustar takakitakakiimport venusian def foo(wrapped): def bar(scanner, name, wrapped): scanner.config.a = scanner.a venusian.attach(wrapped, bar) return wrapped @foo def hello(): pass hello() # appease coverage pyramid-1.4.5/pyramid/tests/test_config/pkgs/__init__.py0000664000175000017500000000001312203712502022636 0ustar takakitakaki# package pyramid-1.4.5/pyramid/tests/test_config/pkgs/selfscan/0000775000175000017500000000000012210157153022334 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/selfscan/another.py0000664000175000017500000000016612203712502024346 0ustar takakitakakifrom pyramid.view import view_config @view_config(name='two', renderer='string') def two(request): return 'two' pyramid-1.4.5/pyramid/tests/test_config/pkgs/selfscan/__init__.py0000664000175000017500000000032412203712502024441 0ustar takakitakakifrom pyramid.view import view_config @view_config(renderer='string') def abc(request): return 'root' def main(): from pyramid.config import Configurator c = Configurator() c.scan() return c pyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/0000775000175000017500000000000012210157153021655 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/models.py0000664000175000017500000000014712203712502023511 0ustar takakitakakifrom zope.interface import Interface class IFixture(Interface): pass def fixture(): """ """ pyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/__init__.py0000664000175000017500000000002012203712502023753 0ustar takakitakaki# package pyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/templates/0000775000175000017500000000000012210157153023653 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/templates/fixture.pt0000664000175000017500000000020512203712502025700 0ustar takakitakaki pyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/views.py0000664000175000017500000000072112203712502023361 0ustar takakitakakifrom zope.interface import Interface from webob import Response from pyramid.httpexceptions import HTTPForbidden def fixture_view(context, request): """ """ return Response('fixture') def erroneous_view(context, request): """ """ raise RuntimeError() def exception_view(context, request): """ """ return Response('supressed') def protected_view(context, request): """ """ raise HTTPForbidden() class IDummy(Interface): pass pyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/subpackage/0000775000175000017500000000000012210157153023762 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/subpackage/__init__.py0000664000175000017500000000001112203712502026060 0ustar takakitakaki#package pyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/subpackage/templates/0000775000175000017500000000000012210157153025760 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/asset/subpackage/templates/bar.pt0000664000175000017500000000001712203712502027064 0ustar takakitakaki pyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/0000775000175000017500000000000012210157153022464 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/another.py0000664000175000017500000000374512203712502024504 0ustar takakitakakifrom pyramid.view import view_config from pyramid.renderers import null_renderer @view_config(name='another', renderer=null_renderer) def grokked(context, request): return 'another_grokked' @view_config(request_method='POST', name='another', renderer=null_renderer) def grokked_post(context, request): return 'another_grokked_post' @view_config(name='another_stacked2', renderer=null_renderer) @view_config(name='another_stacked1', renderer=null_renderer) def stacked(context, request): return 'another_stacked' class stacked_class(object): def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'another_stacked_class' stacked_class = view_config(name='another_stacked_class1', renderer=null_renderer)(stacked_class) stacked_class = view_config(name='another_stacked_class2', renderer=null_renderer)(stacked_class) class oldstyle_grokked_class: def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'another_oldstyle_grokked_class' oldstyle_grokked_class = view_config(name='another_oldstyle_grokked_class', renderer=null_renderer)( oldstyle_grokked_class) class grokked_class(object): def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'another_grokked_class' grokked_class = view_config(name='another_grokked_class', renderer=null_renderer)(grokked_class) class Foo(object): def __call__(self, context, request): return 'another_grokked_instance' grokked_instance = Foo() grokked_instance = view_config(name='another_grokked_instance', renderer=null_renderer)( grokked_instance) # ungrokkable A = 1 B = {} def stuff(): """ """ pyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/pod/0000775000175000017500000000000012210157153023246 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/pod/notinit.py0000664000175000017500000000031612203712502025301 0ustar takakitakakifrom pyramid.view import view_config from pyramid.renderers import null_renderer @view_config(name='pod_notinit', renderer=null_renderer) def subpackage_notinit(context, request): return 'pod_notinit' pyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/__init__.py0000664000175000017500000000503312203712502024573 0ustar takakitakakifrom pyramid.view import view_config from pyramid.renderers import null_renderer @view_config(renderer=null_renderer) def grokked(context, request): return 'grokked' @view_config(request_method='POST', renderer=null_renderer) def grokked_post(context, request): return 'grokked_post' @view_config(name='stacked2', renderer=null_renderer) @view_config(name='stacked1', renderer=null_renderer) def stacked(context, request): return 'stacked' class stacked_class(object): def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'stacked_class' stacked_class = view_config(name='stacked_class1', renderer=null_renderer)(stacked_class) stacked_class = view_config(name='stacked_class2', renderer=null_renderer)(stacked_class) class oldstyle_grokked_class: def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'oldstyle_grokked_class' oldstyle_grokked_class = view_config(name='oldstyle_grokked_class', renderer=null_renderer)( oldstyle_grokked_class) class grokked_class(object): def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'grokked_class' grokked_class = view_config(name='grokked_class', renderer=null_renderer)(grokked_class) class Foo(object): def __call__(self, context, request): return 'grokked_instance' grokked_instance = Foo() grokked_instance = view_config(name='grokked_instance', renderer=null_renderer)(grokked_instance) class Base(object): @view_config(name='basemethod', renderer=null_renderer) def basemethod(self): """ """ class MethodViews(Base): def __init__(self, context, request): self.context = context self.request = request @view_config(name='method1', renderer=null_renderer) def method1(self): return 'method1' @view_config(name='method2', renderer=null_renderer) def method2(self): return 'method2' @view_config(name='stacked_method2', renderer=null_renderer) @view_config(name='stacked_method1', renderer=null_renderer) def stacked(self): return 'stacked_method' # ungrokkable A = 1 B = {} def stuff(): """ """ class Whatever(object): pass class Whatever2: pass pyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/subpackage/0000775000175000017500000000000012210157153024571 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/subpackage/notinit.py0000664000175000017500000000033412203712502026624 0ustar takakitakakifrom pyramid.view import view_config from pyramid.renderers import null_renderer @view_config(name='subpackage_notinit', renderer=null_renderer) def subpackage_notinit(context, request): return 'subpackage_notinit' pyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/subpackage/subsubpackage/0000775000175000017500000000000012210157153027410 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py0000664000175000017500000000033112203712502031513 0ustar takakitakakifrom pyramid.view import view_config from pyramid.renderers import null_renderer @view_config(name='subsubpackage_init', renderer=null_renderer) def subpackage_init(context, request): return 'subsubpackage_init' pyramid-1.4.5/pyramid/tests/test_config/pkgs/scannable/subpackage/__init__.py0000664000175000017500000000032312203712502026675 0ustar takakitakakifrom pyramid.view import view_config from pyramid.renderers import null_renderer @view_config(name='subpackage_init', renderer=null_renderer) def subpackage_init(context, request): return 'subpackage_init' pyramid-1.4.5/pyramid/tests/test_config/test_security.py0000664000175000017500000001023312203712502023046 0ustar takakitakakiimport unittest from pyramid.exceptions import ConfigurationExecutionError from pyramid.exceptions import ConfigurationError class ConfiguratorSecurityMethodsTests(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_set_authentication_policy_no_authz_policy(self): config = self._makeOne() policy = object() config.set_authentication_policy(policy) self.assertRaises(ConfigurationExecutionError, config.commit) def test_set_authentication_policy_no_authz_policy_autocommit(self): config = self._makeOne(autocommit=True) policy = object() self.assertRaises(ConfigurationError, config.set_authentication_policy, policy) def test_set_authentication_policy_with_authz_policy(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy config = self._makeOne() authn_policy = object() authz_policy = object() config.registry.registerUtility(authz_policy, IAuthorizationPolicy) config.set_authentication_policy(authn_policy) config.commit() self.assertEqual( config.registry.getUtility(IAuthenticationPolicy), authn_policy) def test_set_authentication_policy_with_authz_policy_autocommit(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy config = self._makeOne(autocommit=True) authn_policy = object() authz_policy = object() config.registry.registerUtility(authz_policy, IAuthorizationPolicy) config.set_authentication_policy(authn_policy) config.commit() self.assertEqual( config.registry.getUtility(IAuthenticationPolicy), authn_policy) def test_set_authorization_policy_no_authn_policy(self): config = self._makeOne() policy = object() config.set_authorization_policy(policy) self.assertRaises(ConfigurationExecutionError, config.commit) def test_set_authorization_policy_no_authn_policy_autocommit(self): from pyramid.interfaces import IAuthorizationPolicy config = self._makeOne(autocommit=True) policy = object() config.set_authorization_policy(policy) self.assertEqual( config.registry.getUtility(IAuthorizationPolicy), policy) def test_set_authorization_policy_with_authn_policy(self): from pyramid.interfaces import IAuthorizationPolicy from pyramid.interfaces import IAuthenticationPolicy config = self._makeOne() authn_policy = object() authz_policy = object() config.registry.registerUtility(authn_policy, IAuthenticationPolicy) config.set_authorization_policy(authz_policy) config.commit() self.assertEqual( config.registry.getUtility(IAuthorizationPolicy), authz_policy) def test_set_authorization_policy_with_authn_policy_autocommit(self): from pyramid.interfaces import IAuthorizationPolicy from pyramid.interfaces import IAuthenticationPolicy config = self._makeOne(autocommit=True) authn_policy = object() authz_policy = object() config.registry.registerUtility(authn_policy, IAuthenticationPolicy) config.set_authorization_policy(authz_policy) self.assertEqual( config.registry.getUtility(IAuthorizationPolicy), authz_policy) def test_set_default_permission(self): from pyramid.interfaces import IDefaultPermission config = self._makeOne(autocommit=True) config.set_default_permission('view') self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') def test_add_permission(self): config = self._makeOne(autocommit=True) config.add_permission('perm') cat = config.registry.introspector.get_category('permissions') self.assertEqual(len(cat), 1) D = cat[0] intr = D['introspectable'] self.assertEqual(intr['value'], 'perm') pyramid-1.4.5/pyramid/tests/test_config/test_rendering.py0000664000175000017500000000362012203712502023156 0ustar takakitakakiimport unittest import warnings from pyramid.tests.test_config import dummyfactory class TestRenderingConfiguratorMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_set_renderer_globals_factory(self): from pyramid.interfaces import IRendererGlobalsFactory config = self._makeOne(autocommit=True) factory = object() with warnings.catch_warnings(): warnings.filterwarnings('ignore') config.set_renderer_globals_factory(factory) self.assertEqual( config.registry.getUtility(IRendererGlobalsFactory), factory) def test_set_renderer_globals_factory_dottedname(self): from pyramid.interfaces import IRendererGlobalsFactory config = self._makeOne(autocommit=True) with warnings.catch_warnings(): warnings.filterwarnings('ignore') config.set_renderer_globals_factory( 'pyramid.tests.test_config.dummyfactory') self.assertEqual( config.registry.getUtility(IRendererGlobalsFactory), dummyfactory) def test_add_renderer(self): from pyramid.interfaces import IRendererFactory config = self._makeOne(autocommit=True) renderer = object() config.add_renderer('name', renderer) self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), renderer) def test_add_renderer_dottedname_factory(self): from pyramid.interfaces import IRendererFactory config = self._makeOne(autocommit=True) import pyramid.tests.test_config config.add_renderer('name', 'pyramid.tests.test_config') self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), pyramid.tests.test_config) pyramid-1.4.5/pyramid/tests/test_config/test_tweens.py0000664000175000017500000004163012203712502022511 0ustar takakitakakiimport unittest from pyramid.tests.test_config import dummy_tween_factory from pyramid.tests.test_config import dummy_tween_factory2 from pyramid.exceptions import ConfigurationConflictError class TestTweensConfiguratorMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_add_tweens_names_distinct(self): from pyramid.interfaces import ITweens from pyramid.tweens import excview_tween_factory def factory1(handler, registry): return handler def factory2(handler, registry): return handler config = self._makeOne() config.add_tween( 'pyramid.tests.test_config.dummy_tween_factory') config.add_tween( 'pyramid.tests.test_config.dummy_tween_factory2') config.commit() tweens = config.registry.queryUtility(ITweens) implicit = tweens.implicit() self.assertEqual( implicit, [ ('pyramid.tests.test_config.dummy_tween_factory2', dummy_tween_factory2), ('pyramid.tests.test_config.dummy_tween_factory', dummy_tween_factory), ('pyramid.tweens.excview_tween_factory', excview_tween_factory), ] ) def test_add_tweens_names_with_underover(self): from pyramid.interfaces import ITweens from pyramid.tweens import excview_tween_factory from pyramid.tweens import MAIN config = self._makeOne() config.add_tween( 'pyramid.tests.test_config.dummy_tween_factory', over=MAIN) config.add_tween( 'pyramid.tests.test_config.dummy_tween_factory2', over=MAIN, under='pyramid.tests.test_config.dummy_tween_factory') config.commit() tweens = config.registry.queryUtility(ITweens) implicit = tweens.implicit() self.assertEqual( implicit, [ ('pyramid.tweens.excview_tween_factory', excview_tween_factory), ('pyramid.tests.test_config.dummy_tween_factory', dummy_tween_factory), ('pyramid.tests.test_config.dummy_tween_factory2', dummy_tween_factory2), ]) def test_add_tweens_names_with_under_nonstringoriter(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises( ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', under=False) def test_add_tweens_names_with_over_nonstringoriter(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises( ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', over=False) def test_add_tween_dottedname(self): from pyramid.interfaces import ITweens from pyramid.tweens import excview_tween_factory config = self._makeOne() config.add_tween('pyramid.tests.test_config.dummy_tween_factory') config.commit() tweens = config.registry.queryUtility(ITweens) self.assertEqual( tweens.implicit(), [ ('pyramid.tests.test_config.dummy_tween_factory', dummy_tween_factory), ('pyramid.tweens.excview_tween_factory', excview_tween_factory), ]) def test_add_tween_instance(self): from pyramid.exceptions import ConfigurationError class ATween(object): pass atween = ATween() config = self._makeOne() self.assertRaises(ConfigurationError, config.add_tween, atween) def test_add_tween_unsuitable(self): from pyramid.exceptions import ConfigurationError import pyramid.tests.test_config config = self._makeOne() self.assertRaises(ConfigurationError, config.add_tween, pyramid.tests.test_config) def test_add_tween_name_ingress(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import INGRESS config = self._makeOne() self.assertRaises(ConfigurationError, config.add_tween, INGRESS) def test_add_tween_name_main(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN config = self._makeOne() self.assertRaises(ConfigurationError, config.add_tween, MAIN) def test_add_tweens_conflict(self): config = self._makeOne() config.add_tween('pyramid.tests.test_config.dummy_tween_factory') config.add_tween('pyramid.tests.test_config.dummy_tween_factory') self.assertRaises(ConfigurationConflictError, config.commit) def test_add_tween_over_ingress(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import INGRESS config = self._makeOne() self.assertRaises( ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', over=INGRESS) def test_add_tween_over_ingress_iterable(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import INGRESS config = self._makeOne() self.assertRaises( ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', over=('a', INGRESS)) def test_add_tween_under_main(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN config = self._makeOne() self.assertRaises( ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', under=MAIN) def test_add_tween_under_main_iterable(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN config = self._makeOne() self.assertRaises( ConfigurationError, config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory', under=('a', MAIN)) class TestTweens(unittest.TestCase): def _makeOne(self): from pyramid.config.tweens import Tweens return Tweens() def test_add_explicit(self): tweens = self._makeOne() tweens.add_explicit('name', 'factory') self.assertEqual(tweens.explicit, [('name', 'factory')]) tweens.add_explicit('name2', 'factory2') self.assertEqual(tweens.explicit, [('name', 'factory'), ('name2', 'factory2')]) def test_add_implicit(self): tweens = self._makeOne() tweens.add_implicit('name', 'factory') tweens.add_implicit('name2', 'factory2') self.assertEqual(tweens.sorter.sorted(), [('name2', 'factory2'), ('name', 'factory')]) def test___call___explicit(self): tweens = self._makeOne() def factory1(handler, registry): return handler def factory2(handler, registry): return '123' tweens.explicit = [('name', factory1), ('name', factory2)] self.assertEqual(tweens(None, None), '123') def test___call___implicit(self): tweens = self._makeOne() def factory1(handler, registry): return handler def factory2(handler, registry): return '123' tweens.add_implicit('name2', factory2) tweens.add_implicit('name1', factory1) self.assertEqual(tweens(None, None), '123') def test_implicit_ordering_1(self): tweens = self._makeOne() tweens.add_implicit('name1', 'factory1') tweens.add_implicit('name2', 'factory2') self.assertEqual(tweens.implicit(), [ ('name2', 'factory2'), ('name1', 'factory1'), ]) def test_implicit_ordering_2(self): from pyramid.tweens import MAIN tweens = self._makeOne() tweens.add_implicit('name1', 'factory1') tweens.add_implicit('name2', 'factory2', over=MAIN) self.assertEqual(tweens.implicit(), [ ('name1', 'factory1'), ('name2', 'factory2'), ]) def test_implicit_ordering_3(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('auth', 'auth_factory', under='browserid') add('dbt', 'dbt_factory') add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('browserid', 'browserid_factory') add('txnmgr', 'txnmgr_factory', under='exceptionview') add('exceptionview', 'excview_factory', over=MAIN) self.assertEqual(tweens.implicit(), [ ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('dbt', 'dbt_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ('txnmgr', 'txnmgr_factory'), ]) def test_implicit_ordering_4(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('browserid', 'browserid_factory') add('txnmgr', 'txnmgr_factory', under='exceptionview') add('dbt', 'dbt_factory') self.assertEqual(tweens.implicit(), [ ('dbt', 'dbt_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ('txnmgr', 'txnmgr_factory'), ]) def test_implicit_ordering_5(self): from pyramid.tweens import MAIN, INGRESS tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('auth', 'auth_factory', under=INGRESS) add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('browserid', 'browserid_factory', under=INGRESS) add('txnmgr', 'txnmgr_factory', under='exceptionview', over=MAIN) add('dbt', 'dbt_factory') self.assertEqual(tweens.implicit(), [ ('dbt', 'dbt_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ('txnmgr', 'txnmgr_factory'), ]) def test_implicit_ordering_missing_over_partial(self): from pyramid.exceptions import ConfigurationError tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='txnmgr', under='exceptionview') add('browserid', 'browserid_factory') self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_missing_under_partial(self): from pyramid.exceptions import ConfigurationError tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='txnmgr') add('retry', 'retry_factory', over='dbt', under='exceptionview') add('browserid', 'browserid_factory') self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_missing_over_and_under_partials(self): from pyramid.exceptions import ConfigurationError tweens = self._makeOne() add = tweens.add_implicit add('dbt', 'dbt_factory') add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over='foo', under='txnmgr') add('browserid', 'browserid_factory') self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_missing_over_partial_with_fallback(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('auth', 'auth_factory', under='browserid') add('retry', 'retry_factory', over=('txnmgr',MAIN), under='exceptionview') add('browserid', 'browserid_factory') add('dbt', 'dbt_factory') self.assertEqual(tweens.implicit(), [ ('dbt', 'dbt_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) def test_implicit_ordering_missing_under_partial_with_fallback(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('auth', 'auth_factory', under=('txnmgr','browserid')) add('retry', 'retry_factory', under='exceptionview') add('browserid', 'browserid_factory') add('dbt', 'dbt_factory') self.assertEqual(tweens.implicit(), [ ('dbt', 'dbt_factory'), ('browserid', 'browserid_factory'), ('auth', 'auth_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) def test_implicit_ordering_with_partial_fallbacks(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=('wontbethere', MAIN)) add('retry', 'retry_factory', under='exceptionview') add('browserid', 'browserid_factory', over=('wont2', 'exceptionview')) self.assertEqual(tweens.implicit(), [ ('browserid', 'browserid_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) def test_implicit_ordering_with_multiple_matching_fallbacks(self): from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('retry', 'retry_factory', under='exceptionview') add('browserid', 'browserid_factory', over=('retry', 'exceptionview')) self.assertEqual(tweens.implicit(), [ ('browserid', 'browserid_factory'), ('exceptionview', 'excview_factory'), ('retry', 'retry_factory'), ]) def test_implicit_ordering_with_missing_fallbacks(self): from pyramid.exceptions import ConfigurationError from pyramid.tweens import MAIN tweens = self._makeOne() add = tweens.add_implicit add('exceptionview', 'excview_factory', over=MAIN) add('retry', 'retry_factory', under='exceptionview') add('browserid', 'browserid_factory', over=('txnmgr', 'auth')) self.assertRaises(ConfigurationError, tweens.implicit) def test_implicit_ordering_conflict_direct(self): from pyramid.exceptions import CyclicDependencyError tweens = self._makeOne() add = tweens.add_implicit add('browserid', 'browserid_factory') add('auth', 'auth_factory', over='browserid', under='browserid') self.assertRaises(CyclicDependencyError, tweens.implicit) def test_implicit_ordering_conflict_indirect(self): from pyramid.exceptions import CyclicDependencyError tweens = self._makeOne() add = tweens.add_implicit add('browserid', 'browserid_factory') add('auth', 'auth_factory', over='browserid') add('dbt', 'dbt_factory', under='browserid', over='auth') self.assertRaises(CyclicDependencyError, tweens.implicit) pyramid-1.4.5/pyramid/tests/test_config/test_testing.py0000664000175000017500000002055112203712502022660 0ustar takakitakakiimport unittest from pyramid.compat import text_ from pyramid.tests.test_config import IDummy class TestingConfiguratorMixinTests(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_testing_securitypolicy(self): from pyramid.testing import DummySecurityPolicy config = self._makeOne(autocommit=True) config.testing_securitypolicy('user', ('group1', 'group2'), permissive=False) from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy ut = config.registry.getUtility(IAuthenticationPolicy) self.assertTrue(isinstance(ut, DummySecurityPolicy)) ut = config.registry.getUtility(IAuthorizationPolicy) self.assertEqual(ut.userid, 'user') self.assertEqual(ut.groupids, ('group1', 'group2')) self.assertEqual(ut.permissive, False) def test_testing_securitypolicy_remember_result(self): from pyramid.security import remember config = self._makeOne(autocommit=True) pol = config.testing_securitypolicy( 'user', ('group1', 'group2'), permissive=False, remember_result=True) request = DummyRequest() request.registry = config.registry val = remember(request, 'fred') self.assertEqual(pol.remembered, 'fred') self.assertEqual(val, True) def test_testing_securitypolicy_forget_result(self): from pyramid.security import forget config = self._makeOne(autocommit=True) pol = config.testing_securitypolicy( 'user', ('group1', 'group2'), permissive=False, forget_result=True) request = DummyRequest() request.registry = config.registry val = forget(request) self.assertEqual(pol.forgotten, True) self.assertEqual(val, True) def test_testing_resources(self): from pyramid.traversal import find_resource from pyramid.interfaces import ITraverser ob1 = object() ob2 = object() resources = {'/ob1':ob1, '/ob2':ob2} config = self._makeOne(autocommit=True) config.testing_resources(resources) adapter = config.registry.getAdapter(None, ITraverser) result = adapter(DummyRequest({'PATH_INFO':'/ob1'})) self.assertEqual(result['context'], ob1) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('ob1'),)) self.assertEqual(result['virtual_root'], ob1) self.assertEqual(result['virtual_root_path'], ()) result = adapter(DummyRequest({'PATH_INFO':'/ob2'})) self.assertEqual(result['context'], ob2) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('ob2'),)) self.assertEqual(result['virtual_root'], ob2) self.assertEqual(result['virtual_root_path'], ()) self.assertRaises(KeyError, adapter, DummyRequest({'PATH_INFO':'/ob3'})) try: config.begin() self.assertEqual(find_resource(None, '/ob1'), ob1) finally: config.end() def test_testing_add_subscriber_single(self): config = self._makeOne(autocommit=True) L = config.testing_add_subscriber(IDummy) event = DummyEvent() config.registry.notify(event) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.notify(object()) self.assertEqual(len(L), 1) def test_testing_add_subscriber_dottedname(self): config = self._makeOne(autocommit=True) L = config.testing_add_subscriber( 'pyramid.tests.test_config.test_init.IDummy') event = DummyEvent() config.registry.notify(event) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.notify(object()) self.assertEqual(len(L), 1) def test_testing_add_subscriber_multiple(self): from zope.interface import Interface config = self._makeOne(autocommit=True) L = config.testing_add_subscriber((Interface, IDummy)) event = DummyEvent() event.object = 'foo' # the below is the equivalent of z.c.event.objectEventNotify(event) config.registry.subscribers((event.object, event), None) self.assertEqual(len(L), 2) self.assertEqual(L[0], 'foo') self.assertEqual(L[1], event) def test_testing_add_subscriber_defaults(self): config = self._makeOne(autocommit=True) L = config.testing_add_subscriber() event = object() config.registry.notify(event) self.assertEqual(L[-1], event) event2 = object() config.registry.notify(event2) self.assertEqual(L[-1], event2) def test_testing_add_renderer(self): config = self._makeOne(autocommit=True) renderer = config.testing_add_renderer('templates/foo.pt') from pyramid.testing import DummyTemplateRenderer self.assertTrue(isinstance(renderer, DummyTemplateRenderer)) from pyramid.renderers import render_to_response # must provide request to pass in registry (this is a functest) request = DummyRequest() request.registry = config.registry render_to_response( 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) renderer.assert_(foo=1) renderer.assert_(bar=2) renderer.assert_(request=request) def test_testing_add_renderer_twice(self): config = self._makeOne(autocommit=True) renderer1 = config.testing_add_renderer('templates/foo.pt') renderer2 = config.testing_add_renderer('templates/bar.pt') from pyramid.testing import DummyTemplateRenderer self.assertTrue(isinstance(renderer1, DummyTemplateRenderer)) self.assertTrue(isinstance(renderer2, DummyTemplateRenderer)) from pyramid.renderers import render_to_response # must provide request to pass in registry (this is a functest) request = DummyRequest() request.registry = config.registry render_to_response( 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) renderer1.assert_(foo=1) renderer1.assert_(bar=2) renderer1.assert_(request=request) render_to_response( 'templates/bar.pt', {'foo':1, 'bar':2}, request=request) renderer2.assert_(foo=1) renderer2.assert_(bar=2) renderer2.assert_(request=request) def test_testing_add_renderer_explicitrenderer(self): config = self._makeOne(autocommit=True) class E(Exception): pass def renderer(kw, system): self.assertEqual(kw, {'foo':1, 'bar':2}) raise E renderer = config.testing_add_renderer('templates/foo.pt', renderer) from pyramid.renderers import render_to_response # must provide request to pass in registry (this is a functest) request = DummyRequest() request.registry = config.registry try: render_to_response( 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) except E: pass else: # pragma: no cover raise AssertionError def test_testing_add_template(self): config = self._makeOne(autocommit=True) renderer = config.testing_add_template('templates/foo.pt') from pyramid.testing import DummyTemplateRenderer self.assertTrue(isinstance(renderer, DummyTemplateRenderer)) from pyramid.renderers import render_to_response # must provide request to pass in registry (this is a functest) request = DummyRequest() request.registry = config.registry render_to_response('templates/foo.pt', dict(foo=1, bar=2), request=request) renderer.assert_(foo=1) renderer.assert_(bar=2) renderer.assert_(request=request) from zope.interface import implementer @implementer(IDummy) class DummyEvent: pass class DummyRequest: subpath = () matchdict = None def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ self.params = {} self.cookies = {} pyramid-1.4.5/pyramid/tests/test_config/files/0000775000175000017500000000000012210157153020674 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/files/assets/0000775000175000017500000000000012210157153022176 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/files/assets/dummy.txt0000664000175000017500000000000712203712502024064 0ustar takakitakakiHello. pyramid-1.4.5/pyramid/tests/test_config/files/minimal.pt0000664000175000017500000000014612203712502022665 0ustar takakitakaki
pyramid-1.4.5/pyramid/tests/test_config/test_adapters.py0000664000175000017500000003347112203712502023013 0ustar takakitakakiimport unittest from pyramid.compat import PY3 from pyramid.tests.test_config import IDummy class AdaptersConfiguratorMixinTests(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_add_subscriber_defaults(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: pass L = [] def subscriber(event): L.append(event) config = self._makeOne(autocommit=True) config.add_subscriber(subscriber) event = Event() config.registry.notify(event) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.notify(object()) self.assertEqual(len(L), 2) def test_add_subscriber_iface_specified(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: pass L = [] def subscriber(event): L.append(event) config = self._makeOne(autocommit=True) config.add_subscriber(subscriber, IEvent) event = Event() config.registry.notify(event) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.notify(object()) self.assertEqual(len(L), 1) def test_add_subscriber_dottednames(self): import pyramid.tests.test_config from pyramid.interfaces import INewRequest config = self._makeOne(autocommit=True) config.add_subscriber('pyramid.tests.test_config', 'pyramid.interfaces.INewRequest') handlers = list(config.registry.registeredHandlers()) self.assertEqual(len(handlers), 1) handler = handlers[0] self.assertEqual(handler.handler, pyramid.tests.test_config) self.assertEqual(handler.required, (INewRequest,)) def test_add_object_event_subscriber(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: object = 'foo' event = Event() L = [] def subscriber(object, event): L.append(event) config = self._makeOne(autocommit=True) config.add_subscriber(subscriber, (Interface, IEvent)) config.registry.subscribers((event.object, event), None) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.subscribers((event.object, IDummy), None) self.assertEqual(len(L), 1) def test_add_subscriber_with_specific_type_and_predicates_True(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: pass L = [] def subscriber(event): L.append(event) config = self._makeOne(autocommit=True) predlist = config.get_predlist('subscriber') jam_predicate = predicate_maker('jam') jim_predicate = predicate_maker('jim') predlist.add('jam', jam_predicate) predlist.add('jim', jim_predicate) config.add_subscriber(subscriber, IEvent, jam=True, jim=True) event = Event() event.jam = True event.jim = True config.registry.notify(event) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.notify(object()) self.assertEqual(len(L), 1) def test_add_subscriber_with_default_type_predicates_True(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: pass L = [] def subscriber(event): L.append(event) config = self._makeOne(autocommit=True) predlist = config.get_predlist('subscriber') jam_predicate = predicate_maker('jam') jim_predicate = predicate_maker('jim') predlist.add('jam', jam_predicate) predlist.add('jim', jim_predicate) config.add_subscriber(subscriber, jam=True, jim=True) event = Event() event.jam = True event.jim = True config.registry.notify(event) self.assertEqual(len(L), 1) self.assertEqual(L[0], event) config.registry.notify(object()) self.assertEqual(len(L), 1) def test_add_subscriber_with_specific_type_and_predicates_False(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: pass L = [] def subscriber(event): L.append(event) config = self._makeOne(autocommit=True) predlist = config.get_predlist('subscriber') jam_predicate = predicate_maker('jam') jim_predicate = predicate_maker('jim') predlist.add('jam', jam_predicate) predlist.add('jim', jim_predicate) config.add_subscriber(subscriber, IEvent, jam=True, jim=True) event = Event() event.jam = True event.jim = False config.registry.notify(event) self.assertEqual(len(L), 0) def test_add_subscriber_with_default_type_predicates_False(self): from zope.interface import implementer from zope.interface import Interface class IEvent(Interface): pass @implementer(IEvent) class Event: pass L = [] def subscriber(event): L.append(event) config = self._makeOne(autocommit=True) predlist = config.get_predlist('subscriber') jam_predicate = predicate_maker('jam') jim_predicate = predicate_maker('jim') predlist.add('jam', jam_predicate) predlist.add('jim', jim_predicate) config.add_subscriber(subscriber, jam=True, jim=True) event = Event() event.jam = False event.jim = True config.registry.notify(event) self.assertEqual(len(L), 0) def test_add_subscriber_predicate(self): config = self._makeOne() L = [] def add_predicate(type, name, factory, weighs_less_than=None, weighs_more_than=None): self.assertEqual(type, 'subscriber') self.assertEqual(name, 'name') self.assertEqual(factory, 'factory') self.assertEqual(weighs_more_than, 1) self.assertEqual(weighs_less_than, 2) L.append(1) config._add_predicate = add_predicate config.add_subscriber_predicate('name', 'factory', 1, 2) self.assertTrue(L) def test_add_response_adapter(self): from pyramid.interfaces import IResponse config = self._makeOne(autocommit=True) class Adapter(object): def __init__(self, other): self.other = other config.add_response_adapter(Adapter, str) result = config.registry.queryAdapter('foo', IResponse) self.assertTrue(result.other, 'foo') def test_add_response_adapter_self(self): from pyramid.interfaces import IResponse config = self._makeOne(autocommit=True) class Adapter(object): pass config.add_response_adapter(None, Adapter) adapter = Adapter() result = config.registry.queryAdapter(adapter, IResponse) self.assertTrue(result is adapter) def test_add_response_adapter_dottednames(self): from pyramid.interfaces import IResponse config = self._makeOne(autocommit=True) if PY3: # pragma: no cover str_name = 'builtins.str' else: str_name = '__builtin__.str' config.add_response_adapter('pyramid.response.Response', str_name) result = config.registry.queryAdapter('foo', IResponse) self.assertTrue(result.body, b'foo') def test_add_traverser_dotted_names(self): from pyramid.interfaces import ITraverser config = self._makeOne(autocommit=True) config.add_traverser( 'pyramid.tests.test_config.test_adapters.DummyTraverser', 'pyramid.tests.test_config.test_adapters.DummyIface') iface = DummyIface() traverser = config.registry.getAdapter(iface, ITraverser) self.assertEqual(traverser.__class__, DummyTraverser) self.assertEqual(traverser.root, iface) def test_add_traverser_default_iface_means_Interface(self): from pyramid.interfaces import ITraverser config = self._makeOne(autocommit=True) config.add_traverser(DummyTraverser) traverser = config.registry.getAdapter(None, ITraverser) self.assertEqual(traverser.__class__, DummyTraverser) def test_add_traverser_nondefault_iface(self): from pyramid.interfaces import ITraverser config = self._makeOne(autocommit=True) config.add_traverser(DummyTraverser, DummyIface) iface = DummyIface() traverser = config.registry.getAdapter(iface, ITraverser) self.assertEqual(traverser.__class__, DummyTraverser) self.assertEqual(traverser.root, iface) def test_add_traverser_introspectables(self): config = self._makeOne() config.add_traverser(DummyTraverser, DummyIface) actions = config.action_state.actions self.assertEqual(len(actions), 1) intrs = actions[0]['introspectables'] self.assertEqual(len(intrs), 1) intr = intrs[0] self.assertEqual(intr.type_name, 'traverser') self.assertEqual(intr.discriminator, ('traverser', DummyIface)) self.assertEqual(intr.category_name, 'traversers') self.assertEqual(intr.title, 'traverser for %r' % DummyIface) self.assertEqual(intr['adapter'], DummyTraverser) self.assertEqual(intr['iface'], DummyIface) def test_add_resource_url_adapter_dotted_names(self): from pyramid.interfaces import IResourceURL config = self._makeOne(autocommit=True) config.add_resource_url_adapter( 'pyramid.tests.test_config.test_adapters.DummyResourceURL', 'pyramid.tests.test_config.test_adapters.DummyIface', ) iface = DummyIface() adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL) self.assertEqual(adapter.__class__, DummyResourceURL) self.assertEqual(adapter.resource, iface) self.assertEqual(adapter.request, iface) def test_add_resource_url_default_resource_iface_means_Interface(self): from pyramid.interfaces import IResourceURL config = self._makeOne(autocommit=True) config.add_resource_url_adapter(DummyResourceURL) iface = DummyIface() adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL) self.assertEqual(adapter.__class__, DummyResourceURL) self.assertEqual(adapter.resource, iface) self.assertEqual(adapter.request, iface) def test_add_resource_url_nodefault_resource_iface(self): from zope.interface import Interface from pyramid.interfaces import IResourceURL config = self._makeOne(autocommit=True) config.add_resource_url_adapter(DummyResourceURL, DummyIface) iface = DummyIface() adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL) self.assertEqual(adapter.__class__, DummyResourceURL) self.assertEqual(adapter.resource, iface) self.assertEqual(adapter.request, iface) bad_result = config.registry.queryMultiAdapter( (Interface, Interface), IResourceURL, ) self.assertEqual(bad_result, None) def test_add_resource_url_adapter_introspectables(self): config = self._makeOne() config.add_resource_url_adapter(DummyResourceURL, DummyIface) actions = config.action_state.actions self.assertEqual(len(actions), 1) intrs = actions[0]['introspectables'] self.assertEqual(len(intrs), 1) intr = intrs[0] self.assertEqual(intr.type_name, 'resource url adapter') self.assertEqual(intr.discriminator, ('resource url adapter', DummyIface)) self.assertEqual(intr.category_name, 'resource url adapters') self.assertEqual( intr.title, "resource url adapter for resource iface " "" ) self.assertEqual(intr['adapter'], DummyResourceURL) self.assertEqual(intr['resource_iface'], DummyIface) class Test_eventonly(unittest.TestCase): def _callFUT(self, callee): from pyramid.config.adapters import eventonly return eventonly(callee) def test_defaults(self): def acallable(event, a=1, b=2): pass self.assertTrue(self._callFUT(acallable)) class DummyTraverser(object): def __init__(self, root): self.root = root class DummyIface(object): pass class DummyResourceURL(object): def __init__(self, resource, request): self.resource = resource self.request = request def predicate_maker(name): class Predicate(object): def __init__(self, val, config): self.val = val def phash(self): return 'phash' text = phash def __call__(self, event): return getattr(event, name, None) == self.val return Predicate pyramid-1.4.5/pyramid/tests/test_config/__init__.py0000664000175000017500000000217112203712502021701 0ustar takakitakaki# package from zope.interface import implementer from zope.interface import Interface class IFactory(Interface): pass def dummy_tween_factory(handler, registry): pass def dummy_tween_factory2(handler, registry): pass def dummy_include(config): config.registry.included = True config.action('discrim', None, config.package) def dummy_include2(config): config.registry.also_included = True config.action('discrim', None, config.package) includeme = dummy_include class DummyContext: pass @implementer(IFactory) class DummyFactory(object): def __call__(self): """ """ def dummyfactory(request): """ """ class IDummy(Interface): pass def dummy_view(request): return 'OK' def dummy_extend(config, discrim): config.action(discrim, None, config.package) def dummy_extend2(config, discrim): config.action(discrim, None, config.registry) from functools import partial dummy_partial = partial(dummy_extend, discrim='partial') class DummyCallable(object): def __call__(self, config, discrim): config.action(discrim, None, config.package) dummy_callable = DummyCallable() pyramid-1.4.5/pyramid/tests/test_config/test_assets.py0000664000175000017500000006636712203712502022524 0ustar takakitakakiimport unittest from pyramid.testing import cleanUp class TestAssetsConfiguratorMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_override_asset_samename(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset,'a', 'a') def test_override_asset_directory_with_file(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset, 'a:foo/', 'a:foo.pt') def test_override_asset_file_with_directory(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset, 'a:foo.pt', 'a:foo/') def test_override_asset_file_with_package(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset, 'a:foo.pt', 'a') def test_override_asset_file_with_file(self): config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( 'pyramid.tests.test_config.pkgs.asset:templates/foo.pt', 'pyramid.tests.test_config.pkgs.asset.subpackage:templates/bar.pt', _override=override) from pyramid.tests.test_config.pkgs import asset from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, 'templates/foo.pt') self.assertEqual(override.override_package, subpackage) self.assertEqual(override.override_prefix, 'templates/bar.pt') def test_override_asset_package_with_package(self): config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( 'pyramid.tests.test_config.pkgs.asset', 'pyramid.tests.test_config.pkgs.asset.subpackage', _override=override) from pyramid.tests.test_config.pkgs import asset from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, '') self.assertEqual(override.override_package, subpackage) self.assertEqual(override.override_prefix, '') def test_override_asset_directory_with_directory(self): config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( 'pyramid.tests.test_config.pkgs.asset:templates/', 'pyramid.tests.test_config.pkgs.asset.subpackage:templates/', _override=override) from pyramid.tests.test_config.pkgs import asset from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, 'templates/') self.assertEqual(override.override_package, subpackage) self.assertEqual(override.override_prefix, 'templates/') def test_override_asset_directory_with_package(self): config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( 'pyramid.tests.test_config.pkgs.asset:templates/', 'pyramid.tests.test_config.pkgs.asset.subpackage', _override=override) from pyramid.tests.test_config.pkgs import asset from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, 'templates/') self.assertEqual(override.override_package, subpackage) self.assertEqual(override.override_prefix, '') def test_override_asset_package_with_directory(self): config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( 'pyramid.tests.test_config.pkgs.asset', 'pyramid.tests.test_config.pkgs.asset.subpackage:templates/', _override=override) from pyramid.tests.test_config.pkgs import asset from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, '') self.assertEqual(override.override_package, subpackage) self.assertEqual(override.override_prefix, 'templates/') def test__override_not_yet_registered(self): from pyramid.interfaces import IPackageOverrides package = DummyPackage('package') opackage = DummyPackage('opackage') config = self._makeOne() config._override(package, 'path', opackage, 'oprefix', PackageOverrides=DummyPackageOverrides) overrides = config.registry.queryUtility(IPackageOverrides, name='package') self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) self.assertEqual(overrides.package, package) def test__override_already_registered(self): from pyramid.interfaces import IPackageOverrides package = DummyPackage('package') opackage = DummyPackage('opackage') overrides = DummyPackageOverrides(package) config = self._makeOne() config.registry.registerUtility(overrides, IPackageOverrides, name='package') config._override(package, 'path', opackage, 'oprefix', PackageOverrides=DummyPackageOverrides) self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) self.assertEqual(overrides.package, package) class TestOverrideProvider(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _getTargetClass(self): from pyramid.config.assets import OverrideProvider return OverrideProvider def _makeOne(self, module): klass = self._getTargetClass() return klass(module) def _registerOverrides(self, overrides, name='pyramid.tests.test_config'): from pyramid.interfaces import IPackageOverrides from pyramid.threadlocal import get_current_registry reg = get_current_registry() reg.registerUtility(overrides, IPackageOverrides, name=name) def test_get_resource_filename_no_overrides(self): import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) here = os.path.dirname(os.path.abspath(__file__)) expected = os.path.join(here, resource_name) result = provider.get_resource_filename(None, resource_name) self.assertEqual(result, expected) def test_get_resource_stream_no_overrides(self): import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) here = os.path.dirname(os.path.abspath(__file__)) with provider.get_resource_stream(None, resource_name) as result: _assertBody(result.read(), os.path.join(here, resource_name)) def test_get_resource_string_no_overrides(self): import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) here = os.path.dirname(os.path.abspath(__file__)) result = provider.get_resource_string(None, resource_name) _assertBody(result, os.path.join(here, resource_name)) def test_has_resource_no_overrides(self): resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) result = provider.has_resource(resource_name) self.assertEqual(result, True) def test_resource_isdir_no_overrides(self): file_resource_name = 'test_assets.py' directory_resource_name = 'files' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) result = provider.resource_isdir(file_resource_name) self.assertEqual(result, False) result = provider.resource_isdir(directory_resource_name) self.assertEqual(result, True) def test_resource_listdir_no_overrides(self): resource_name = 'files' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) result = provider.resource_listdir(resource_name) self.assertTrue(result) def test_get_resource_filename_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) here = os.path.dirname(os.path.abspath(__file__)) expected = os.path.join(here, resource_name) result = provider.get_resource_filename(None, resource_name) self.assertEqual(result, expected) def test_get_resource_stream_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) here = os.path.dirname(os.path.abspath(__file__)) with provider.get_resource_stream(None, resource_name) as result: _assertBody(result.read(), os.path.join(here, resource_name)) def test_get_resource_string_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) here = os.path.dirname(os.path.abspath(__file__)) result = provider.get_resource_string(None, resource_name) _assertBody(result, os.path.join(here, resource_name)) def test_has_resource_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) result = provider.has_resource(resource_name) self.assertEqual(result, True) def test_resource_isdir_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) resource_name = 'files' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) result = provider.resource_isdir(resource_name) self.assertEqual(result, True) def test_resource_listdir_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) resource_name = 'files' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) result = provider.resource_listdir(resource_name) self.assertTrue(result) def test_get_resource_filename_override_returns_value(self): overrides = DummyOverrides('value') import pyramid.tests.test_config self._registerOverrides(overrides) provider = self._makeOne(pyramid.tests.test_config) result = provider.get_resource_filename(None, 'test_assets.py') self.assertEqual(result, 'value') def test_get_resource_stream_override_returns_value(self): from io import BytesIO overrides = DummyOverrides(BytesIO(b'value')) import pyramid.tests.test_config self._registerOverrides(overrides) provider = self._makeOne(pyramid.tests.test_config) with provider.get_resource_stream(None, 'test_assets.py') as stream: self.assertEqual(stream.getvalue(), b'value') def test_get_resource_string_override_returns_value(self): overrides = DummyOverrides('value') import pyramid.tests.test_config self._registerOverrides(overrides) provider = self._makeOne(pyramid.tests.test_config) result = provider.get_resource_string(None, 'test_assets.py') self.assertEqual(result, 'value') def test_has_resource_override_returns_True(self): overrides = DummyOverrides(True) import pyramid.tests.test_config self._registerOverrides(overrides) provider = self._makeOne(pyramid.tests.test_config) result = provider.has_resource('test_assets.py') self.assertEqual(result, True) def test_resource_isdir_override_returns_False(self): overrides = DummyOverrides(False) import pyramid.tests.test_config self._registerOverrides(overrides) provider = self._makeOne(pyramid.tests.test_config) result = provider.resource_isdir('files') self.assertEqual(result, False) def test_resource_listdir_override_returns_values(self): overrides = DummyOverrides(['a']) import pyramid.tests.test_config self._registerOverrides(overrides) provider = self._makeOne(pyramid.tests.test_config) result = provider.resource_listdir('files') self.assertEqual(result, ['a']) class TestPackageOverrides(unittest.TestCase): def _getTargetClass(self): from pyramid.config.assets import PackageOverrides return PackageOverrides def _makeOne(self, package=None, pkg_resources=None): if package is None: package = DummyPackage('package') klass = self._getTargetClass() if pkg_resources is None: pkg_resources = DummyPkgResources() return klass(package, pkg_resources=pkg_resources) def test_class_conforms_to_IPackageOverrides(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IPackageOverrides verifyClass(IPackageOverrides, self._getTargetClass()) def test_instance_conforms_to_IPackageOverrides(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IPackageOverrides verifyObject(IPackageOverrides, self._makeOne()) def test_class_conforms_to_IPEP302Loader(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IPEP302Loader verifyClass(IPEP302Loader, self._getTargetClass()) def test_instance_conforms_to_IPEP302Loader(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IPEP302Loader verifyObject(IPEP302Loader, self._makeOne()) def test_ctor_package_already_has_loader_of_different_type(self): package = DummyPackage('package') loader = package.__loader__ = DummyLoader() po = self._makeOne(package) self.assertTrue(package.__loader__ is po) self.assertTrue(po.real_loader is loader) def test_ctor_package_already_has_loader_of_same_type(self): package = DummyPackage('package') package.__loader__ = self._makeOne(package) po = self._makeOne(package) self.assertEqual(package.__loader__, po) def test_ctor_sets_loader(self): package = DummyPackage('package') po = self._makeOne(package) self.assertEqual(package.__loader__, po) def test_ctor_registers_loader_type(self): from pyramid.config.assets import OverrideProvider dummy_pkg_resources = DummyPkgResources() package = DummyPackage('package') po = self._makeOne(package, dummy_pkg_resources) self.assertEqual(dummy_pkg_resources.registered, [(po.__class__, OverrideProvider)]) def test_ctor_sets_local_state(self): package = DummyPackage('package') po = self._makeOne(package) self.assertEqual(po.overrides, []) self.assertEqual(po.overridden_package_name, 'package') def test_insert_directory(self): from pyramid.config.assets import DirectoryOverride package = DummyPackage('package') po = self._makeOne(package) po.overrides= [None] po.insert('foo/', 'package', 'bar/') self.assertEqual(len(po.overrides), 2) override = po.overrides[0] self.assertEqual(override.__class__, DirectoryOverride) def test_insert_file(self): from pyramid.config.assets import FileOverride package = DummyPackage('package') po = self._makeOne(package) po.overrides= [None] po.insert('foo.pt', 'package', 'bar.pt') self.assertEqual(len(po.overrides), 2) override = po.overrides[0] self.assertEqual(override.__class__, FileOverride) def test_insert_emptystring(self): # XXX is this a valid case for a directory? from pyramid.config.assets import DirectoryOverride package = DummyPackage('package') po = self._makeOne(package) po.overrides= [None] po.insert('', 'package', 'bar/') self.assertEqual(len(po.overrides), 2) override = po.overrides[0] self.assertEqual(override.__class__, DirectoryOverride) def test_search_path(self): overrides = [ DummyOverride(None), DummyOverride(('package', 'name'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(list(po.search_path('whatever')), [('package', 'name')]) def test_get_filename(self): import os overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'test_assets.py'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides here = os.path.dirname(os.path.abspath(__file__)) expected = os.path.join(here, 'test_assets.py') self.assertEqual(po.get_filename('whatever'), expected) def test_get_filename_file_doesnt_exist(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.get_filename('whatever'), None) def test_get_stream(self): import os overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'test_assets.py'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides here = os.path.dirname(os.path.abspath(__file__)) with po.get_stream('whatever') as stream: _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) def test_get_stream_file_doesnt_exist(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.get_stream('whatever'), None) def test_get_string(self): import os overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'test_assets.py'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides here = os.path.dirname(os.path.abspath(__file__)) _assertBody(po.get_string('whatever'), os.path.join(here, 'test_assets.py')) def test_get_string_file_doesnt_exist(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.get_string('whatever'), None) def test_has_resource(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'test_assets.py'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.has_resource('whatever'), True) def test_has_resource_file_doesnt_exist(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.has_resource('whatever'), None) def test_isdir_false(self): overrides = [ DummyOverride( ('pyramid.tests.test_config', 'test_assets.py'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.isdir('whatever'), False) def test_isdir_true(self): overrides = [ DummyOverride( ('pyramid.tests.test_config', 'files'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.isdir('whatever'), True) def test_isdir_doesnt_exist(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.isdir('whatever'), None) def test_listdir(self): overrides = [ DummyOverride( ('pyramid.tests.test_config', 'files'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertTrue(po.listdir('whatever')) def test_listdir_doesnt_exist(self): overrides = [ DummyOverride(None), DummyOverride( ('pyramid.tests.test_config', 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) po.overrides= overrides self.assertEqual(po.listdir('whatever'), None) # PEP 302 __loader__ extensions: use the "real" __loader__, if present. def test_get_data_pkg_has_no___loader__(self): package = DummyPackage('package') po = self._makeOne(package) self.assertRaises(NotImplementedError, po.get_data, 'whatever') def test_get_data_pkg_has___loader__(self): package = DummyPackage('package') loader = package.__loader__ = DummyLoader() po = self._makeOne(package) self.assertEqual(po.get_data('whatever'), b'DEADBEEF') self.assertEqual(loader._got_data, 'whatever') def test_is_package_pkg_has_no___loader__(self): package = DummyPackage('package') po = self._makeOne(package) self.assertRaises(NotImplementedError, po.is_package, 'whatever') def test_is_package_pkg_has___loader__(self): package = DummyPackage('package') loader = package.__loader__ = DummyLoader() po = self._makeOne(package) self.assertTrue(po.is_package('whatever')) self.assertEqual(loader._is_package, 'whatever') def test_get_code_pkg_has_no___loader__(self): package = DummyPackage('package') po = self._makeOne(package) self.assertRaises(NotImplementedError, po.get_code, 'whatever') def test_get_code_pkg_has___loader__(self): package = DummyPackage('package') loader = package.__loader__ = DummyLoader() po = self._makeOne(package) self.assertEqual(po.get_code('whatever'), b'DEADBEEF') self.assertEqual(loader._got_code, 'whatever') def test_get_source_pkg_has_no___loader__(self): package = DummyPackage('package') po = self._makeOne(package) self.assertRaises(NotImplementedError, po.get_source, 'whatever') def test_get_source_pkg_has___loader__(self): package = DummyPackage('package') loader = package.__loader__ = DummyLoader() po = self._makeOne(package) self.assertEqual(po.get_source('whatever'), 'def foo():\n pass') self.assertEqual(loader._got_source, 'whatever') class TestDirectoryOverride(unittest.TestCase): def _getTargetClass(self): from pyramid.config.assets import DirectoryOverride return DirectoryOverride def _makeOne(self, path, package, prefix): klass = self._getTargetClass() return klass(path, package, prefix) def test_it_match(self): o = self._makeOne('foo/', 'package', 'bar/') result = o('foo/something.pt') self.assertEqual(result, ('package', 'bar/something.pt')) def test_it_no_match(self): o = self._makeOne('foo/', 'package', 'bar/') result = o('baz/notfound.pt') self.assertEqual(result, None) class TestFileOverride(unittest.TestCase): def _getTargetClass(self): from pyramid.config.assets import FileOverride return FileOverride def _makeOne(self, path, package, prefix): klass = self._getTargetClass() return klass(path, package, prefix) def test_it_match(self): o = self._makeOne('foo.pt', 'package', 'bar.pt') result = o('foo.pt') self.assertEqual(result, ('package', 'bar.pt')) def test_it_no_match(self): o = self._makeOne('foo.pt', 'package', 'bar.pt') result = o('notfound.pt') self.assertEqual(result, None) class DummyOverride: def __init__(self, result): self.result = result def __call__(self, resource_name): return self.result class DummyOverrides: def __init__(self, result): self.result = result def get_filename(self, resource_name): return self.result listdir = isdir = has_resource = get_stream = get_string = get_filename class DummyPackageOverrides: def __init__(self, package): self.package = package self.inserted = [] def insert(self, path, package, prefix): self.inserted.append((path, package, prefix)) class DummyPkgResources: def __init__(self): self.registered = [] def register_loader_type(self, typ, inst): self.registered.append((typ, inst)) class DummyPackage: def __init__(self, name): self.__name__ = name class DummyLoader: _got_data = _is_package = None def get_data(self, path): self._got_data = path return b'DEADBEEF' def is_package(self, fullname): self._is_package = fullname return True def get_code(self, fullname): self._got_code = fullname return b'DEADBEEF' def get_source(self, fullname): self._got_source = fullname return 'def foo():\n pass' class DummyUnderOverride: def __call__(self, package, path, override_package, override_prefix, _info=''): self.package = package self.path = path self.override_package = override_package self.override_prefix = override_prefix def read_(src): with open(src, 'rb') as f: contents = f.read() return contents def _assertBody(body, filename): # strip both \n and \r for windows body = body.replace(b'\r', b'') body = body.replace(b'\n', b'') data = read_(filename) data = data.replace(b'\r', b'') data = data.replace(b'\n', b'') assert(body == data) pyramid-1.4.5/pyramid/tests/test_config/test_i18n.py0000664000175000017500000001163512210154276021773 0ustar takakitakakiimport os import unittest from pyramid.tests.test_config import dummyfactory here = os.path.dirname(__file__) locale = os.path.abspath( os.path.join(here, '..', 'pkgs', 'localeapp', 'locale')) locale2 = os.path.abspath( os.path.join(here, '..', 'pkgs', 'localeapp', 'locale2')) locale3 = os.path.abspath( os.path.join(here, '..', 'pkgs', 'localeapp', 'locale3')) class TestI18NConfiguratorMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_set_locale_negotiator(self): from pyramid.interfaces import ILocaleNegotiator config = self._makeOne(autocommit=True) def negotiator(request): pass config.set_locale_negotiator(negotiator) self.assertEqual(config.registry.getUtility(ILocaleNegotiator), negotiator) def test_set_locale_negotiator_dottedname(self): from pyramid.interfaces import ILocaleNegotiator config = self._makeOne(autocommit=True) config.set_locale_negotiator( 'pyramid.tests.test_config.dummyfactory') self.assertEqual(config.registry.getUtility(ILocaleNegotiator), dummyfactory) def test_add_translation_dirs_missing_dir(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.add_translation_dirs, '/wont/exist/on/my/system') def test_add_translation_dirs_no_specs(self): from pyramid.interfaces import ITranslationDirectories from pyramid.interfaces import IChameleonTranslate config = self._makeOne() config.add_translation_dirs() self.assertEqual(config.registry.queryUtility(ITranslationDirectories), None) self.assertEqual(config.registry.queryUtility(IChameleonTranslate), None) def test_add_translation_dirs_asset_spec(self): from pyramid.interfaces import ITranslationDirectories config = self._makeOne(autocommit=True) config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale') self.assertEqual(config.registry.getUtility(ITranslationDirectories), [locale]) def test_add_translation_dirs_asset_spec_existing_translation_dirs(self): from pyramid.interfaces import ITranslationDirectories config = self._makeOne(autocommit=True) directories = ['abc'] config.registry.registerUtility(directories, ITranslationDirectories) config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale') result = config.registry.getUtility(ITranslationDirectories) self.assertEqual(result, [locale, 'abc']) def test_add_translation_dirs_multiple_specs(self): from pyramid.interfaces import ITranslationDirectories config = self._makeOne(autocommit=True) config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale', 'pyramid.tests.pkgs.localeapp:locale2') self.assertEqual(config.registry.getUtility(ITranslationDirectories), [locale, locale2]) def test_add_translation_dirs_multiple_specs_multiple_calls(self): from pyramid.interfaces import ITranslationDirectories config = self._makeOne(autocommit=True) config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale', 'pyramid.tests.pkgs.localeapp:locale2') config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale3') self.assertEqual(config.registry.getUtility(ITranslationDirectories), [locale3, locale, locale2]) def test_add_translation_dirs_registers_chameleon_translate(self): from pyramid.interfaces import IChameleonTranslate from pyramid.threadlocal import manager request = DummyRequest() config = self._makeOne(autocommit=True) manager.push({'request':request, 'registry':config.registry}) try: config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale') translate = config.registry.getUtility(IChameleonTranslate) self.assertEqual(translate('Approve'), 'Approve') finally: manager.pop() def test_add_translation_dirs_abspath(self): from pyramid.interfaces import ITranslationDirectories config = self._makeOne(autocommit=True) config.add_translation_dirs(locale) self.assertEqual(config.registry.getUtility(ITranslationDirectories), [locale]) class DummyRequest: subpath = () matchdict = None def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ self.params = {} self.cookies = {} pyramid-1.4.5/pyramid/tests/test_config/path/0000775000175000017500000000000012210157152020525 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/path/scanerror/0000775000175000017500000000000012210157153022524 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_config/path/scanerror/will_raise_error.py0000664000175000017500000000002212203712502026430 0ustar takakitakakiimport wont.exist pyramid-1.4.5/pyramid/tests/test_config/path/scanerror/__init__.py0000664000175000017500000000002712203712502024631 0ustar takakitakaki# scan error package pyramid-1.4.5/pyramid/tests/test_config/test_factories.py0000664000175000017500000001534112203712502023163 0ustar takakitakakiimport unittest from pyramid.tests.test_config import dummyfactory class TestFactoriesMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def test_set_request_factory(self): from pyramid.interfaces import IRequestFactory config = self._makeOne(autocommit=True) factory = object() config.set_request_factory(factory) self.assertEqual(config.registry.getUtility(IRequestFactory), factory) def test_set_request_factory_dottedname(self): from pyramid.interfaces import IRequestFactory config = self._makeOne(autocommit=True) config.set_request_factory( 'pyramid.tests.test_config.dummyfactory') self.assertEqual(config.registry.getUtility(IRequestFactory), dummyfactory) def test_set_root_factory(self): from pyramid.interfaces import IRootFactory config = self._makeOne() config.set_root_factory(dummyfactory) self.assertEqual(config.registry.queryUtility(IRootFactory), None) config.commit() self.assertEqual(config.registry.getUtility(IRootFactory), dummyfactory) def test_set_root_factory_as_None(self): from pyramid.interfaces import IRootFactory from pyramid.traversal import DefaultRootFactory config = self._makeOne() config.set_root_factory(None) self.assertEqual(config.registry.queryUtility(IRootFactory), None) config.commit() self.assertEqual(config.registry.getUtility(IRootFactory), DefaultRootFactory) def test_set_root_factory_dottedname(self): from pyramid.interfaces import IRootFactory config = self._makeOne() config.set_root_factory('pyramid.tests.test_config.dummyfactory') self.assertEqual(config.registry.queryUtility(IRootFactory), None) config.commit() self.assertEqual(config.registry.getUtility(IRootFactory), dummyfactory) def test_set_session_factory(self): from pyramid.interfaces import ISessionFactory config = self._makeOne() config.set_session_factory(dummyfactory) self.assertEqual(config.registry.queryUtility(ISessionFactory), None) config.commit() self.assertEqual(config.registry.getUtility(ISessionFactory), dummyfactory) def test_set_session_factory_dottedname(self): from pyramid.interfaces import ISessionFactory config = self._makeOne() config.set_session_factory('pyramid.tests.test_config.dummyfactory') self.assertEqual(config.registry.queryUtility(ISessionFactory), None) config.commit() self.assertEqual(config.registry.getUtility(ISessionFactory), dummyfactory) def test_set_request_property_with_callable(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne(autocommit=True) callable = lambda x: None config.set_request_property(callable, name='foo') exts = config.registry.getUtility(IRequestExtensions) self.assertTrue('foo' in exts.descriptors) def test_set_request_property_with_unnamed_callable(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne(autocommit=True) def foo(self): pass config.set_request_property(foo, reify=True) exts = config.registry.getUtility(IRequestExtensions) self.assertTrue('foo' in exts.descriptors) def test_set_request_property_with_property(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne(autocommit=True) callable = property(lambda x: None) config.set_request_property(callable, name='foo') exts = config.registry.getUtility(IRequestExtensions) self.assertTrue('foo' in exts.descriptors) def test_set_multiple_request_properties(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne() def foo(self): pass bar = property(lambda x: None) config.set_request_property(foo, reify=True) config.set_request_property(bar, name='bar') config.commit() exts = config.registry.getUtility(IRequestExtensions) self.assertTrue('foo' in exts.descriptors) self.assertTrue('bar' in exts.descriptors) def test_set_multiple_request_properties_conflict(self): from pyramid.exceptions import ConfigurationConflictError config = self._makeOne() def foo(self): pass bar = property(lambda x: None) config.set_request_property(foo, name='bar', reify=True) config.set_request_property(bar, name='bar') self.assertRaises(ConfigurationConflictError, config.commit) def test_add_request_method_with_callable(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne(autocommit=True) callable = lambda x: None config.add_request_method(callable, name='foo') exts = config.registry.getUtility(IRequestExtensions) self.assertTrue('foo' in exts.methods) def test_add_request_method_with_unnamed_callable(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne(autocommit=True) def foo(self): pass config.add_request_method(foo) exts = config.registry.getUtility(IRequestExtensions) self.assertTrue('foo' in exts.methods) def test_set_multiple_request_methods_conflict(self): from pyramid.exceptions import ConfigurationConflictError config = self._makeOne() def foo(self): pass def bar(self): pass config.add_request_method(foo, name='bar') config.add_request_method(bar, name='bar') self.assertRaises(ConfigurationConflictError, config.commit) def test_add_request_method_with_None_callable(self): from pyramid.interfaces import IRequestExtensions config = self._makeOne(autocommit=True) config.add_request_method(name='foo') exts = config.registry.queryUtility(IRequestExtensions) self.assertTrue(exts is None) def test_add_request_method_with_None_callable_conflict(self): from pyramid.exceptions import ConfigurationConflictError config = self._makeOne() def bar(self): pass config.add_request_method(name='foo') config.add_request_method(bar, name='foo') self.assertRaises(ConfigurationConflictError, config.commit) def test_add_request_method_with_None_callable_and_no_name(self): config = self._makeOne(autocommit=True) self.assertRaises(AttributeError, config.add_request_method) pyramid-1.4.5/pyramid/tests/test_config/test_views.py0000664000175000017500000047740512210154276022364 0ustar takakitakakiimport unittest from pyramid import testing from pyramid.tests.test_config import IDummy from pyramid.tests.test_config import dummy_view from pyramid.compat import ( im_func, text_, ) from pyramid.exceptions import ConfigurationError from pyramid.exceptions import ConfigurationExecutionError from pyramid.exceptions import ConfigurationConflictError class TestViewsConfigurationMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator config = Configurator(*arg, **kw) return config def _getViewCallable(self, config, ctx_iface=None, request_iface=None, name='', exception_view=False): from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier if exception_view: classifier = IExceptionViewClassifier else: classifier = IViewClassifier if ctx_iface is None: ctx_iface = Interface if request_iface is None: request_iface = IRequest return config.registry.adapters.lookup( (classifier, request_iface, ctx_iface), IView, name=name, default=None) def _registerRenderer(self, config, name='.txt'): from pyramid.interfaces import IRendererFactory from pyramid.interfaces import ITemplateRenderer from zope.interface import implementer @implementer(ITemplateRenderer) class Renderer: def __init__(self, info): self.__class__.info = info def __call__(self, *arg): return 'Hello!' config.registry.registerUtility(Renderer, IRendererFactory, name=name) return Renderer def _makeRequest(self, config): request = DummyRequest() request.registry = config.registry return request def _assertNotFound(self, wrapper, *arg): from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, wrapper, *arg) def _getRouteRequestIface(self, config, name): from pyramid.interfaces import IRouteRequest iface = config.registry.getUtility(IRouteRequest, name) return iface def _assertRoute(self, config, name, path, num_predicates=0): from pyramid.interfaces import IRoutesMapper mapper = config.registry.getUtility(IRoutesMapper) routes = mapper.get_routes() route = routes[0] self.assertEqual(len(routes), 1) self.assertEqual(route.name, name) self.assertEqual(route.path, path) self.assertEqual(len(routes[0].predicates), num_predicates) return route def test_add_view_view_callable_None_no_renderer(self): config = self._makeOne(autocommit=True) self.assertRaises(ConfigurationError, config.add_view) def test_add_view_with_request_type_and_route_name(self): config = self._makeOne(autocommit=True) view = lambda *arg: 'OK' self.assertRaises(ConfigurationError, config.add_view, view, '', None, None, True, True) def test_add_view_with_request_type(self): from pyramid.renderers import null_renderer from zope.interface import directlyProvides from pyramid.interfaces import IRequest view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_type='pyramid.interfaces.IRequest', renderer=null_renderer) wrapper = self._getViewCallable(config) request = DummyRequest() self._assertNotFound(wrapper, None, request) directlyProvides(request, IRequest) result = wrapper(None, request) self.assertEqual(result, 'OK') def test_add_view_view_callable_None_with_renderer(self): config = self._makeOne(autocommit=True) self._registerRenderer(config, name='dummy') config.add_view(renderer='dummy') view = self._getViewCallable(config) self.assertTrue(b'Hello!' in view(None, None).body) def test_add_view_wrapped_view_is_decorated(self): def view(request): # request-only wrapper """ """ config = self._makeOne(autocommit=True) config.add_view(view=view) wrapper = self._getViewCallable(config) self.assertEqual(wrapper.__module__, view.__module__) self.assertEqual(wrapper.__name__, view.__name__) self.assertEqual(wrapper.__doc__, view.__doc__) self.assertEqual(wrapper.__discriminator__(None, None).resolve()[0], 'view') def test_add_view_view_callable_dottedname(self): from pyramid.renderers import null_renderer config = self._makeOne(autocommit=True) config.add_view(view='pyramid.tests.test_config.dummy_view', renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertEqual(wrapper(None, None), 'OK') def test_add_view_with_function_callable(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_with_function_callable_requestonly(self): from pyramid.renderers import null_renderer def view(request): return 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_with_name(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, name='abc', renderer=null_renderer) wrapper = self._getViewCallable(config, name='abc') result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_with_name_unicode(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) name = text_(b'La Pe\xc3\xb1a', 'utf-8') config.add_view(view=view, name=name, renderer=null_renderer) wrapper = self._getViewCallable(config, name=name) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_with_decorator(self): from pyramid.renderers import null_renderer def view(request): """ ABC """ return 'OK' def view_wrapper(fn): def inner(context, request): return fn(context, request) return inner config = self._makeOne(autocommit=True) config.add_view(view=view, decorator=view_wrapper, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertFalse(wrapper is view) self.assertEqual(wrapper.__doc__, view.__doc__) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_with_decorator_tuple(self): from pyramid.renderers import null_renderer def view(request): """ ABC """ return 'OK' def view_wrapper1(fn): def inner(context, request): return 'wrapped1' + fn(context, request) return inner def view_wrapper2(fn): def inner(context, request): return 'wrapped2' + fn(context, request) return inner config = self._makeOne(autocommit=True) config.add_view(view=view, decorator=(view_wrapper2, view_wrapper1), renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertFalse(wrapper is view) self.assertEqual(wrapper.__doc__, view.__doc__) result = wrapper(None, None) self.assertEqual(result, 'wrapped2wrapped1OK') def test_add_view_with_http_cache(self): import datetime from pyramid.response import Response response = Response('OK') def view(request): """ ABC """ return response config = self._makeOne(autocommit=True) config.add_view(view=view, http_cache=(86400, {'public':True})) wrapper = self._getViewCallable(config) self.assertFalse(wrapper is view) self.assertEqual(wrapper.__doc__, view.__doc__) request = testing.DummyRequest() when = datetime.datetime.utcnow() + datetime.timedelta(days=1) result = wrapper(None, request) self.assertEqual(result, response) headers = dict(response.headerlist) self.assertEqual(headers['Cache-Control'], 'max-age=86400, public') expires = parse_httpdate(headers['Expires']) assert_similar_datetime(expires, when) def test_add_view_as_instance(self): from pyramid.renderers import null_renderer class AView: def __call__(self, context, request): """ """ return 'OK' view = AView() config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_as_instancemethod(self): from pyramid.renderers import null_renderer class View: def index(self, context, request): return 'OK' view = View() config=self._makeOne(autocommit=True) config.add_view(view=view.index, renderer=null_renderer) wrapper = self._getViewCallable(config) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_as_instancemethod_requestonly(self): from pyramid.renderers import null_renderer class View: def index(self, request): return 'OK' view = View() config=self._makeOne(autocommit=True) config.add_view(view=view.index, renderer=null_renderer) wrapper = self._getViewCallable(config) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_as_instance_requestonly(self): from pyramid.renderers import null_renderer class AView: def __call__(self, request): """ """ return 'OK' view = AView() config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_as_oldstyle_class(self): from pyramid.renderers import null_renderer class view: def __init__(self, context, request): self.context = context self.request = request def __call__(self): return 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result, 'OK') self.assertEqual(request.__view__.__class__, view) def test_add_view_as_oldstyle_class_requestonly(self): from pyramid.renderers import null_renderer class view: def __init__(self, request): self.request = request def __call__(self): return 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result, 'OK') self.assertEqual(request.__view__.__class__, view) def test_add_view_context_as_class(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy view = lambda *arg: 'OK' class Foo: pass config = self._makeOne(autocommit=True) config.add_view(context=Foo, view=view, renderer=null_renderer) foo = implementedBy(Foo) wrapper = self._getViewCallable(config, foo) self.assertEqual(wrapper, view) def test_add_view_context_as_iface(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(context=IDummy, view=view, renderer=null_renderer) wrapper = self._getViewCallable(config, IDummy) self.assertEqual(wrapper, view) def test_add_view_context_as_dottedname(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(context='pyramid.tests.test_config.IDummy', view=view, renderer=null_renderer) wrapper = self._getViewCallable(config, IDummy) self.assertEqual(wrapper, view) def test_add_view_for__as_dottedname(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(for_='pyramid.tests.test_config.IDummy', view=view, renderer=null_renderer) wrapper = self._getViewCallable(config, IDummy) self.assertEqual(wrapper, view) def test_add_view_for_as_class(self): # ``for_`` is older spelling for ``context`` from pyramid.renderers import null_renderer from zope.interface import implementedBy view = lambda *arg: 'OK' class Foo: pass config = self._makeOne(autocommit=True) config.add_view(for_=Foo, view=view, renderer=null_renderer) foo = implementedBy(Foo) wrapper = self._getViewCallable(config, foo) self.assertEqual(wrapper, view) def test_add_view_for_as_iface(self): # ``for_`` is older spelling for ``context`` from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(for_=IDummy, view=view, renderer=null_renderer) wrapper = self._getViewCallable(config, IDummy) self.assertEqual(wrapper, view) def test_add_view_context_trumps_for(self): # ``for_`` is older spelling for ``context`` from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) class Foo: pass config.add_view(context=IDummy, for_=Foo, view=view, renderer=null_renderer) wrapper = self._getViewCallable(config, IDummy) self.assertEqual(wrapper, view) def test_add_view_register_secured_view(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import ISecuredView from pyramid.interfaces import IViewClassifier view = lambda *arg: 'OK' view.__call_permissive__ = view config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) wrapper = config.registry.adapters.lookup( (IViewClassifier, IRequest, Interface), ISecuredView, name='', default=None) self.assertEqual(wrapper, view) def test_add_view_exception_register_secured_view(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IExceptionViewClassifier view = lambda *arg: 'OK' view.__call_permissive__ = view config = self._makeOne(autocommit=True) config.add_view(view=view, context=RuntimeError, renderer=null_renderer) wrapper = config.registry.adapters.lookup( (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='', default=None) self.assertEqual(wrapper, view) def test_add_view_same_phash_overrides_existing_single_view(self): from pyramid.renderers import null_renderer from hashlib import md5 from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IMultiView phash = md5() phash.update(b'xhr = True') view = lambda *arg: 'NOT OK' view.__phash__ = phash.hexdigest() config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') def newview(context, request): return 'OK' config.add_view(view=newview, xhr=True, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertFalse(IMultiView.providedBy(wrapper)) request = DummyRequest() request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_exc_same_phash_overrides_existing_single_view(self): from pyramid.renderers import null_renderer from hashlib import md5 from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IMultiView phash = md5() phash.update(b'xhr = True') view = lambda *arg: 'NOT OK' view.__phash__ = phash.hexdigest() config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') def newview(context, request): return 'OK' config.add_view(view=newview, xhr=True, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertFalse(IMultiView.providedBy(wrapper)) request = DummyRequest() request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_default_phash_overrides_no_phash(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'NOT OK' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') def newview(context, request): return 'OK' config.add_view(view=newview, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertFalse(IMultiView.providedBy(wrapper)) request = DummyRequest() request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_exc_default_phash_overrides_no_phash(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'NOT OK' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') def newview(context, request): return 'OK' config.add_view(view=newview, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertFalse(IMultiView.providedBy(wrapper)) request = DummyRequest() request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_default_phash_overrides_default_phash(self): from pyramid.renderers import null_renderer from pyramid.config.util import DEFAULT_PHASH from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'NOT OK' view.__phash__ = DEFAULT_PHASH config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') def newview(context, request): return 'OK' config.add_view(view=newview, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertFalse(IMultiView.providedBy(wrapper)) request = DummyRequest() request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_exc_default_phash_overrides_default_phash(self): from pyramid.renderers import null_renderer from pyramid.config.util import DEFAULT_PHASH from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'NOT OK' view.__phash__ = DEFAULT_PHASH config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') def newview(context, request): return 'OK' config.add_view(view=newview, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertFalse(IMultiView.providedBy(wrapper)) request = DummyRequest() request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_multiview_replaces_existing_view(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'OK' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(wrapper(None, None), 'OK') def test_add_view_exc_multiview_replaces_existing_view(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'OK' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') config.add_view(view=view, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(wrapper(None, None), 'OK') def test_add_view_multiview_replaces_existing_securedview(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import ISecuredView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier view = lambda *arg: 'OK' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), ISecuredView, name='') config.add_view(view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(wrapper(None, None), 'OK') def test_add_view_exc_multiview_replaces_existing_securedview(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import ISecuredView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier view = lambda *arg: 'OK' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, implementedBy(RuntimeError)), ISecuredView, name='') config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), ISecuredView, name='') config.add_view(view=view, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(wrapper(None, None), 'OK') def test_add_view_with_accept_multiview_replaces_existing_view(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier def view(context, request): return 'OK' def view2(context, request): return 'OK2' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') config.add_view(view=view2, accept='text/html', renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(len(wrapper.views), 1) self.assertEqual(len(wrapper.media_views), 1) self.assertEqual(wrapper(None, None), 'OK') request = DummyRequest() request.accept = DummyAccept('text/html', 'text/html') self.assertEqual(wrapper(None, request), 'OK2') def test_add_view_mixed_case_replaces_existing_view(self): from pyramid.renderers import null_renderer def view(context, request): return 'OK' def view2(context, request): return 'OK2' def view3(context, request): return 'OK3' config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) config.add_view(view=view2, accept='text/html', renderer=null_renderer) config.add_view(view=view3, accept='text/HTML', renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(len(wrapper.media_views.items()),1) self.assertFalse('text/HTML' in wrapper.media_views) self.assertEqual(wrapper(None, None), 'OK') request = DummyRequest() request.accept = DummyAccept('text/html', 'text/html') self.assertEqual(wrapper(None, request), 'OK3') def test_add_views_with_accept_multiview_replaces_existing(self): from pyramid.renderers import null_renderer def view(context, request): return 'OK' def view2(context, request): return 'OK2' def view3(context, request): return 'OK3' config = self._makeOne(autocommit=True) config.add_view(view=view, renderer=null_renderer) config.add_view(view=view2, accept='text/html', renderer=null_renderer) config.add_view(view=view3, accept='text/html', renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertEqual(len(wrapper.media_views['text/html']), 1) self.assertEqual(wrapper(None, None), 'OK') request = DummyRequest() request.accept = DummyAccept('text/html', 'text/html') self.assertEqual(wrapper(None, request), 'OK3') def test_add_view_exc_with_accept_multiview_replaces_existing_view(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier def view(context, request): return 'OK' def view2(context, request): return 'OK2' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') config.add_view(view=view2, accept='text/html', context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(len(wrapper.views), 1) self.assertEqual(len(wrapper.media_views), 1) self.assertEqual(wrapper(None, None), 'OK') request = DummyRequest() request.accept = DummyAccept('text/html', 'text/html') self.assertEqual(wrapper(None, request), 'OK2') def test_add_view_multiview_replaces_existing_view_with___accept__(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier def view(context, request): return 'OK' def view2(context, request): return 'OK2' view.__accept__ = 'text/html' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') config.add_view(view=view2, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(len(wrapper.views), 1) self.assertEqual(len(wrapper.media_views), 1) self.assertEqual(wrapper(None, None), 'OK2') request = DummyRequest() request.accept = DummyAccept('text/html') self.assertEqual(wrapper(None, request), 'OK') def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier def view(context, request): return 'OK' def view2(context, request): return 'OK2' view.__accept__ = 'text/html' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IView, name='') config.add_view(view=view2, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual(len(wrapper.views), 1) self.assertEqual(len(wrapper.media_views), 1) self.assertEqual(wrapper(None, None), 'OK2') request = DummyRequest() request.accept = DummyAccept('text/html') self.assertEqual(wrapper(None, request), 'OK') def test_add_view_multiview_replaces_multiview(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier view = DummyMultiView() config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IMultiView, name='') view2 = lambda *arg: 'OK2' config.add_view(view=view2, renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) self.assertEqual(wrapper(None, None), 'OK1') def test_add_view_exc_multiview_replaces_multiview(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier view = DummyMultiView() config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, implementedBy(RuntimeError)), IMultiView, name='') config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), IMultiView, name='') view2 = lambda *arg: 'OK2' config.add_view(view=view2, context=RuntimeError, renderer=null_renderer) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), exception_view=True) self.assertTrue(IMultiView.providedBy(wrapper)) self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) self.assertEqual(wrapper(None, None), 'OK1') def test_add_view_multiview_context_superclass_then_subclass(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier class ISuper(Interface): pass class ISub(ISuper): pass view = lambda *arg: 'OK' view2 = lambda *arg: 'OK2' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, ISuper), IView, name='') config.add_view(view=view2, for_=ISub, renderer=null_renderer) wrapper = self._getViewCallable(config, ISuper, IRequest) self.assertFalse(IMultiView.providedBy(wrapper)) self.assertEqual(wrapper(None, None), 'OK') wrapper = self._getViewCallable(config, ISub, IRequest) self.assertFalse(IMultiView.providedBy(wrapper)) self.assertEqual(wrapper(None, None), 'OK2') def test_add_view_multiview_exception_superclass_then_subclass(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IMultiView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier class Super(Exception): pass class Sub(Super): pass view = lambda *arg: 'OK' view2 = lambda *arg: 'OK2' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Super), IView, name='') config.registry.registerAdapter( view, (IExceptionViewClassifier, IRequest, Super), IView, name='') config.add_view(view=view2, for_=Sub, renderer=null_renderer) wrapper = self._getViewCallable( config, implementedBy(Super), IRequest) wrapper_exc_view = self._getViewCallable( config, implementedBy(Super), IRequest, exception_view=True) self.assertEqual(wrapper_exc_view, wrapper) self.assertFalse(IMultiView.providedBy(wrapper_exc_view)) self.assertEqual(wrapper_exc_view(None, None), 'OK') wrapper = self._getViewCallable( config, implementedBy(Sub), IRequest) wrapper_exc_view = self._getViewCallable( config, implementedBy(Sub), IRequest, exception_view=True) self.assertEqual(wrapper_exc_view, wrapper) self.assertFalse(IMultiView.providedBy(wrapper_exc_view)) self.assertEqual(wrapper_exc_view(None, None), 'OK2') def test_add_view_multiview_call_ordering(self): from pyramid.renderers import null_renderer as nr from zope.interface import directlyProvides def view1(context, request): return 'view1' def view2(context, request): return 'view2' def view3(context, request): return 'view3' def view4(context, request): return 'view4' def view5(context, request): return 'view5' def view6(context, request): return 'view6' def view7(context, request): return 'view7' def view8(context, request): return 'view8' config = self._makeOne(autocommit=True) config.add_view(view=view1, renderer=nr) config.add_view(view=view2, request_method='POST', renderer=nr) config.add_view(view=view3,request_param='param', renderer=nr) config.add_view(view=view4, containment=IDummy, renderer=nr) config.add_view(view=view5, request_method='POST', request_param='param', renderer=nr) config.add_view(view=view6, request_method='POST', containment=IDummy, renderer=nr) config.add_view(view=view7, request_param='param', containment=IDummy, renderer=nr) config.add_view(view=view8, request_method='POST',request_param='param', containment=IDummy, renderer=nr) wrapper = self._getViewCallable(config) ctx = DummyContext() request = self._makeRequest(config) request.method = 'GET' request.params = {} self.assertEqual(wrapper(ctx, request), 'view1') ctx = DummyContext() request = self._makeRequest(config) request.params = {} request.method = 'POST' self.assertEqual(wrapper(ctx, request), 'view2') ctx = DummyContext() request = self._makeRequest(config) request.params = {'param':'1'} request.method = 'GET' self.assertEqual(wrapper(ctx, request), 'view3') ctx = DummyContext() directlyProvides(ctx, IDummy) request = self._makeRequest(config) request.method = 'GET' request.params = {} self.assertEqual(wrapper(ctx, request), 'view4') ctx = DummyContext() request = self._makeRequest(config) request.method = 'POST' request.params = {'param':'1'} self.assertEqual(wrapper(ctx, request), 'view5') ctx = DummyContext() directlyProvides(ctx, IDummy) request = self._makeRequest(config) request.params = {} request.method = 'POST' self.assertEqual(wrapper(ctx, request), 'view6') ctx = DummyContext() directlyProvides(ctx, IDummy) request = self._makeRequest(config) request.method = 'GET' request.params = {'param':'1'} self.assertEqual(wrapper(ctx, request), 'view7') ctx = DummyContext() directlyProvides(ctx, IDummy) request = self._makeRequest(config) request.method = 'POST' request.params = {'param':'1'} self.assertEqual(wrapper(ctx, request), 'view8') def test_add_view_multiview___discriminator__(self): from pyramid.renderers import null_renderer from zope.interface import Interface class IFoo(Interface): pass class IBar(Interface): pass @implementer(IFoo) class Foo(object): pass @implementer(IBar) class Bar(object): pass foo = Foo() bar = Bar() from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IMultiView view = lambda *arg: 'OK' view.__phash__ = 'abc' config = self._makeOne(autocommit=True) config.registry.registerAdapter( view, (IViewClassifier, IRequest, Interface), IView, name='') config.add_view(view=view, renderer=null_renderer, containment=IFoo) config.add_view(view=view, renderer=null_renderer, containment=IBar) wrapper = self._getViewCallable(config) self.assertTrue(IMultiView.providedBy(wrapper)) request = self._makeRequest(config) self.assertNotEqual( wrapper.__discriminator__(foo, request), wrapper.__discriminator__(bar, request), ) def test_add_view_with_template_renderer(self): from pyramid.tests import test_config from pyramid.interfaces import ISettings class view(object): def __init__(self, context, request): self.request = request self.context = context def __call__(self): return {'a':'1'} config = self._makeOne(autocommit=True) renderer = self._registerRenderer(config) fixture = 'pyramid.tests.test_config:files/minimal.txt' config.add_view(view=view, renderer=fixture) wrapper = self._getViewCallable(config) request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result.body, b'Hello!') settings = config.registry.queryUtility(ISettings) result = renderer.info self.assertEqual(result.registry, config.registry) self.assertEqual(result.type, '.txt') self.assertEqual(result.package, test_config) self.assertEqual(result.name, fixture) self.assertEqual(result.settings, settings) def test_add_view_with_default_renderer(self): class view(object): def __init__(self, context, request): self.request = request self.context = context def __call__(self): return {'a':'1'} config = self._makeOne(autocommit=True) class moo(object): def __init__(self, *arg, **kw): pass def __call__(self, *arg, **kw): return 'moo' config.add_renderer(None, moo) config.add_view(view=view) wrapper = self._getViewCallable(config) request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result.body, b'moo') def test_add_view_with_template_renderer_no_callable(self): from pyramid.tests import test_config from pyramid.interfaces import ISettings config = self._makeOne(autocommit=True) renderer = self._registerRenderer(config) fixture = 'pyramid.tests.test_config:files/minimal.txt' config.add_view(view=None, renderer=fixture) wrapper = self._getViewCallable(config) request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result.body, b'Hello!') settings = config.registry.queryUtility(ISettings) result = renderer.info self.assertEqual(result.registry, config.registry) self.assertEqual(result.type, '.txt') self.assertEqual(result.package, test_config) self.assertEqual(result.name, fixture) self.assertEqual(result.settings, settings) def test_add_view_with_request_type_as_iface(self): from pyramid.renderers import null_renderer from zope.interface import directlyProvides def view(context, request): return 'OK' config = self._makeOne(autocommit=True) config.add_view(request_type=IDummy, view=view, renderer=null_renderer) wrapper = self._getViewCallable(config, None) request = self._makeRequest(config) directlyProvides(request, IDummy) result = wrapper(None, request) self.assertEqual(result, 'OK') def test_add_view_with_request_type_as_noniface(self): view = lambda *arg: 'OK' config = self._makeOne() self.assertRaises(ConfigurationError, config.add_view, view, '', None, None, object) def test_add_view_with_route_name(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_route('foo', '/a/b') config.add_view(view=view, route_name='foo', renderer=null_renderer) request_iface = self._getRouteRequestIface(config, 'foo') self.assertNotEqual(request_iface, None) wrapper = self._getViewCallable(config, request_iface=request_iface) self.assertNotEqual(wrapper, None) self.assertEqual(wrapper(None, None), 'OK') def test_add_view_with_nonexistant_route_name(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne() config.add_view(view=view, route_name='foo', renderer=null_renderer) self.assertRaises(ConfigurationExecutionError, config.commit) def test_add_view_with_route_name_exception(self): from pyramid.renderers import null_renderer from zope.interface import implementedBy view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_route('foo', '/a/b') config.add_view(view=view, route_name='foo', context=RuntimeError, renderer=null_renderer) request_iface = self._getRouteRequestIface(config, 'foo') wrapper_exc_view = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), request_iface=request_iface, exception_view=True) self.assertNotEqual(wrapper_exc_view, None) wrapper = self._getViewCallable( config, ctx_iface=implementedBy(RuntimeError), request_iface=request_iface) self.assertEqual(wrapper_exc_view, wrapper) self.assertEqual(wrapper_exc_view(None, None), 'OK') def test_add_view_with_request_method_true(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_method='POST', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'POST' self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_request_method_false(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_method='POST') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'GET' self._assertNotFound(wrapper, None, request) def test_add_view_with_request_method_sequence_true(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_method=('POST', 'GET'), renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'POST' self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_request_method_sequence_conflict(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne() config.add_view(view=view, request_method=('POST', 'GET'), renderer=null_renderer) config.add_view(view=view, request_method=('GET', 'POST'), renderer=null_renderer) self.assertRaises(ConfigurationConflictError, config.commit) def test_add_view_with_request_method_sequence_false(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_method=('POST', 'HEAD')) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'GET' self._assertNotFound(wrapper, None, request) def test_add_view_with_request_method_get_implies_head(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_method='GET', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'HEAD' self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_request_param_noval_true(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_param='abc', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.params = {'abc':''} self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_request_param_noval_false(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_param='abc') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.params = {} self._assertNotFound(wrapper, None, request) def test_add_view_with_request_param_val_true(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_param='abc=123', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.params = {'abc':'123'} self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_request_param_val_false(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_param='abc=123') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.params = {'abc':''} self._assertNotFound(wrapper, None, request) def test_add_view_with_xhr_true(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, xhr=True, renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_xhr_false(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, xhr=True) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.is_xhr = False self._assertNotFound(wrapper, None, request) def test_add_view_with_header_badregex(self): view = lambda *arg: 'OK' config = self._makeOne() config.add_view(view, header='Host:a\\') self.assertRaises(ConfigurationError, config.commit) def test_add_view_with_header_noval_match(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, header='Host', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.headers = {'Host':'whatever'} self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_header_noval_nomatch(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, header='Host') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.headers = {'NotHost':'whatever'} self._assertNotFound(wrapper, None, request) def test_add_view_with_header_val_match(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, header=r'Host:\d', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.headers = {'Host':'1'} self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_header_val_nomatch(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, header=r'Host:\d') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.headers = {'Host':'abc'} self._assertNotFound(wrapper, None, request) def test_add_view_with_header_val_missing(self): from pyramid.httpexceptions import HTTPNotFound view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, header=r'Host:\d') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.headers = {'NoHost':'1'} self.assertRaises(HTTPNotFound, wrapper, None, request) def test_add_view_with_accept_match(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, accept='text/xml', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.accept = ['text/xml'] self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_accept_nomatch(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, accept='text/xml') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.accept = ['text/html'] self._assertNotFound(wrapper, None, request) def test_add_view_with_containment_true(self): from pyramid.renderers import null_renderer from zope.interface import directlyProvides view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, containment=IDummy, renderer=null_renderer) wrapper = self._getViewCallable(config) context = DummyContext() directlyProvides(context, IDummy) self.assertEqual(wrapper(context, None), 'OK') def test_add_view_with_containment_false(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, containment=IDummy) wrapper = self._getViewCallable(config) context = DummyContext() self._assertNotFound(wrapper, context, None) def test_add_view_with_containment_dottedname(self): from pyramid.renderers import null_renderer from zope.interface import directlyProvides view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view( view=view, containment='pyramid.tests.test_config.IDummy', renderer=null_renderer) wrapper = self._getViewCallable(config) context = DummyContext() directlyProvides(context, IDummy) self.assertEqual(wrapper(context, None), 'OK') def test_add_view_with_path_info_badregex(self): view = lambda *arg: 'OK' config = self._makeOne() config.add_view(view, path_info='\\') self.assertRaises(ConfigurationError, config.commit) def test_add_view_with_path_info_match(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, path_info='/foo', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.upath_info = text_(b'/foo') self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_path_info_nomatch(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) config.add_view(view=view, path_info='/foo') wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.upath_info = text_('/') self._assertNotFound(wrapper, None, request) def test_add_view_with_custom_predicates_match(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) def pred1(context, request): return True def pred2(context, request): return True predicates = (pred1, pred2) config.add_view(view=view, custom_predicates=predicates, renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) self.assertEqual(wrapper(None, request), 'OK') def test_add_view_with_custom_predicates_nomatch(self): view = lambda *arg: 'OK' config = self._makeOne(autocommit=True) def pred1(context, request): return True def pred2(context, request): return False predicates = (pred1, pred2) config.add_view(view=view, custom_predicates=predicates) wrapper = self._getViewCallable(config) request = self._makeRequest(config) self._assertNotFound(wrapper, None, request) def test_add_view_custom_predicate_bests_standard_predicate(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' view2 = lambda *arg: 'NOT OK' config = self._makeOne(autocommit=True) def pred1(context, request): return True config.add_view(view=view, custom_predicates=(pred1,), renderer=null_renderer) config.add_view(view=view2, request_method='GET', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'GET' self.assertEqual(wrapper(None, request), 'OK') def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' view2 = lambda *arg: 'NOT OK' config = self._makeOne(autocommit=True) config.add_view(view=view, request_method='GET', xhr=True, renderer=null_renderer) config.add_view(view=view2, request_method='GET', renderer=null_renderer) wrapper = self._getViewCallable(config) request = self._makeRequest(config) request.method = 'GET' request.is_xhr = True self.assertEqual(wrapper(None, request), 'OK') def test_add_view_same_predicates(self): view2 = lambda *arg: 'second' view1 = lambda *arg: 'first' config = self._makeOne() config.add_view(view=view1) config.add_view(view=view2) self.assertRaises(ConfigurationConflictError, config.commit) def test_add_view_with_permission(self): from pyramid.renderers import null_renderer view1 = lambda *arg: 'OK' outerself = self class DummyPolicy(object): def effective_principals(self, r): outerself.assertEqual(r, request) return ['abc'] def permits(self, context, principals, permission): outerself.assertEqual(context, None) outerself.assertEqual(principals, ['abc']) outerself.assertEqual(permission, 'view') return True policy = DummyPolicy() config = self._makeOne(authorization_policy=policy, authentication_policy=policy, autocommit=True) config.add_view(view=view1, permission='view', renderer=null_renderer) view = self._getViewCallable(config) request = self._makeRequest(config) self.assertEqual(view(None, request), 'OK') def test_add_view_with_default_permission_no_explicit_permission(self): from pyramid.renderers import null_renderer view1 = lambda *arg: 'OK' outerself = self class DummyPolicy(object): def effective_principals(self, r): outerself.assertEqual(r, request) return ['abc'] def permits(self, context, principals, permission): outerself.assertEqual(context, None) outerself.assertEqual(principals, ['abc']) outerself.assertEqual(permission, 'view') return True policy = DummyPolicy() config = self._makeOne(authorization_policy=policy, authentication_policy=policy, default_permission='view', autocommit=True) config.add_view(view=view1, renderer=null_renderer) view = self._getViewCallable(config) request = self._makeRequest(config) self.assertEqual(view(None, request), 'OK') def test_add_view_with_no_default_permission_no_explicit_permission(self): from pyramid.renderers import null_renderer view1 = lambda *arg: 'OK' class DummyPolicy(object): pass # wont be called policy = DummyPolicy() config = self._makeOne(authorization_policy=policy, authentication_policy=policy, autocommit=True) config.add_view(view=view1, renderer=null_renderer) view = self._getViewCallable(config) request = self._makeRequest(config) self.assertEqual(view(None, request), 'OK') def test_add_view_with_mapper(self): from pyramid.renderers import null_renderer class Mapper(object): def __init__(self, **kw): self.__class__.kw = kw def __call__(self, view): return view config = self._makeOne(autocommit=True) def view(context, request): return 'OK' config.add_view(view=view, mapper=Mapper, renderer=null_renderer) view = self._getViewCallable(config) self.assertEqual(view(None, None), 'OK') self.assertEqual(Mapper.kw['mapper'], Mapper) def test_add_view_with_view_defaults(self): from pyramid.renderers import null_renderer from pyramid.exceptions import PredicateMismatch from zope.interface import directlyProvides class view(object): __view_defaults__ = { 'containment':'pyramid.tests.test_config.IDummy' } def __init__(self, request): pass def __call__(self): return 'OK' config = self._makeOne(autocommit=True) config.add_view( view=view, renderer=null_renderer) wrapper = self._getViewCallable(config) context = DummyContext() directlyProvides(context, IDummy) request = self._makeRequest(config) self.assertEqual(wrapper(context, request), 'OK') context = DummyContext() request = self._makeRequest(config) self.assertRaises(PredicateMismatch, wrapper, context, request) def test_add_view_with_view_defaults_viewname_is_dottedname_kwarg(self): from pyramid.renderers import null_renderer from pyramid.exceptions import PredicateMismatch from zope.interface import directlyProvides config = self._makeOne(autocommit=True) config.add_view( view='pyramid.tests.test_config.test_views.DummyViewDefaultsClass', renderer=null_renderer) wrapper = self._getViewCallable(config) context = DummyContext() directlyProvides(context, IDummy) request = self._makeRequest(config) self.assertEqual(wrapper(context, request), 'OK') context = DummyContext() request = self._makeRequest(config) self.assertRaises(PredicateMismatch, wrapper, context, request) def test_add_view_with_view_defaults_viewname_is_dottedname_nonkwarg(self): from pyramid.renderers import null_renderer from pyramid.exceptions import PredicateMismatch from zope.interface import directlyProvides config = self._makeOne(autocommit=True) config.add_view( 'pyramid.tests.test_config.test_views.DummyViewDefaultsClass', renderer=null_renderer) wrapper = self._getViewCallable(config) context = DummyContext() directlyProvides(context, IDummy) request = self._makeRequest(config) self.assertEqual(wrapper(context, request), 'OK') context = DummyContext() request = self._makeRequest(config) self.assertRaises(PredicateMismatch, wrapper, context, request) def test_add_view_with_view_config_and_view_defaults_doesnt_conflict(self): from pyramid.renderers import null_renderer class view(object): __view_defaults__ = { 'containment':'pyramid.tests.test_config.IDummy' } class view2(object): __view_defaults__ = { 'containment':'pyramid.tests.test_config.IFactory' } config = self._makeOne(autocommit=False) config.add_view( view=view, renderer=null_renderer) config.add_view( view=view2, renderer=null_renderer) config.commit() # does not raise def test_add_view_with_view_config_and_view_defaults_conflicts(self): from pyramid.renderers import null_renderer class view(object): __view_defaults__ = { 'containment':'pyramid.tests.test_config.IDummy' } class view2(object): __view_defaults__ = { 'containment':'pyramid.tests.test_config.IDummy' } config = self._makeOne(autocommit=False) config.add_view( view=view, renderer=null_renderer) config.add_view( view=view2, renderer=null_renderer) self.assertRaises(ConfigurationConflictError, config.commit) def test_derive_view_function(self): from pyramid.renderers import null_renderer def view(request): return 'OK' config = self._makeOne() result = config.derive_view(view, renderer=null_renderer) self.assertFalse(result is view) self.assertEqual(result(None, None), 'OK') def test_derive_view_dottedname(self): from pyramid.renderers import null_renderer config = self._makeOne() result = config.derive_view( 'pyramid.tests.test_config.dummy_view', renderer=null_renderer) self.assertFalse(result is dummy_view) self.assertEqual(result(None, None), 'OK') def test_derive_view_with_default_renderer_no_explicit_renderer(self): config = self._makeOne() class moo(object): def __init__(self, view): pass def __call__(self, *arg, **kw): return 'moo' config.add_renderer(None, moo) config.commit() def view(request): return 'OK' result = config.derive_view(view) self.assertFalse(result is view) self.assertEqual(result(None, None).body, b'moo') def test_derive_view_with_default_renderer_with_explicit_renderer(self): class moo(object): pass class foo(object): def __init__(self, view): pass def __call__(self, *arg, **kw): return 'foo' def view(request): return 'OK' config = self._makeOne() config.add_renderer(None, moo) config.add_renderer('foo', foo) config.commit() result = config.derive_view(view, renderer='foo') self.assertFalse(result is view) request = self._makeRequest(config) self.assertEqual(result(None, request).body, b'foo') def test_add_static_view_here_no_utility_registered(self): from pyramid.renderers import null_renderer from zope.interface import Interface from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier config = self._makeOne(autocommit=True) config.add_static_view('static', 'files', renderer=null_renderer) request_type = self._getRouteRequestIface(config, '__static/') self._assertRoute(config, '__static/', 'static/*subpath') wrapped = config.registry.adapters.lookup( (IViewClassifier, request_type, Interface), IView, name='') from pyramid.request import Request request = Request.blank('/static/minimal.pt') request.subpath = ('minimal.pt', ) result = wrapped(None, request) self.assertEqual(result.status, '200 OK') self.assertTrue(result.body.startswith(b' into a response object. The value returned was None. You ' 'may have forgotten to return a value from the view callable.')) else: # pragma: no cover raise AssertionError def test_function_returns_true_Response_no_renderer(self): from pyramid.response import Response r = Response('Hello') def view(request): return r deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) response = result(None, None) self.assertEqual(response, r) def test_function_returns_true_Response_with_renderer(self): from pyramid.response import Response r = Response('Hello') def view(request): return r renderer = object() deriver = self._makeOne(renderer=renderer) result = deriver(view) self.assertFalse(result is view) response = result(None, None) self.assertEqual(response, r) def test_requestonly_default_method_returns_non_adaptable(self): request = DummyRequest() class AView(object): def __init__(self, request): pass def __call__(self): return None deriver = self._makeOne() result = deriver(AView) self.assertFalse(result is AView) try: result(None, request) except ValueError as e: self.assertEqual( e.args[0], 'Could not convert return value of the view callable ' 'method __call__ of ' 'class pyramid.tests.test_config.test_views.AView into a ' 'response object. The value returned was None. You may have ' 'forgotten to return a value from the view callable.' ) else: # pragma: no cover raise AssertionError def test_requestonly_nondefault_method_returns_non_adaptable(self): request = DummyRequest() class AView(object): def __init__(self, request): pass def theviewmethod(self): return None deriver = self._makeOne(attr='theviewmethod') result = deriver(AView) self.assertFalse(result is AView) try: result(None, request) except ValueError as e: self.assertEqual( e.args[0], 'Could not convert return value of the view callable ' 'method theviewmethod of ' 'class pyramid.tests.test_config.test_views.AView into a ' 'response object. The value returned was None. You may have ' 'forgotten to return a value from the view callable.' ) else: # pragma: no cover raise AssertionError def test_requestonly_function(self): response = DummyResponse() def view(request): return response deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(result(None, None), response) def test_requestonly_function_with_renderer(self): response = DummyResponse() class moo(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, 'OK') self.assertEqual(view_inst, view) self.assertEqual(ctx, context) return response def view(request): return 'OK' deriver = self._makeOne(renderer=moo()) result = deriver(view) self.assertFalse(result.__wraps__ is view) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_requestonly_function_with_renderer_request_override(self): def moo(info): def inner(value, system): self.assertEqual(value, 'OK') self.assertEqual(system['request'], request) self.assertEqual(system['context'], context) return 'moo' return inner def view(request): return 'OK' self.config.add_renderer('moo', moo) deriver = self._makeOne(renderer='string') result = deriver(view) self.assertFalse(result is view) request = self._makeRequest() request.override_renderer = 'moo' context = testing.DummyResource() self.assertEqual(result(context, request).body, b'moo') def test_requestonly_function_with_renderer_request_has_view(self): response = DummyResponse() class moo(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, 'OK') self.assertEqual(view_inst, 'view') self.assertEqual(ctx, context) return response def view(request): return 'OK' deriver = self._makeOne(renderer=moo()) result = deriver(view) self.assertFalse(result.__wraps__ is view) request = self._makeRequest() request.__view__ = 'view' context = testing.DummyResource() r = result(context, request) self.assertEqual(r, response) self.assertFalse(hasattr(request, '__view__')) def test_class_without_attr(self): response = DummyResponse() class View(object): def __init__(self, request): pass def __call__(self): return response deriver = self._makeOne() result = deriver(View) request = self._makeRequest() self.assertEqual(result(None, request), response) self.assertEqual(request.__view__.__class__, View) def test_class_with_attr(self): response = DummyResponse() class View(object): def __init__(self, request): pass def another(self): return response deriver = self._makeOne(attr='another') result = deriver(View) request = self._makeRequest() self.assertEqual(result(None, request), response) self.assertEqual(request.__view__.__class__, View) def test_as_function_context_and_request(self): def view(context, request): return 'OK' deriver = self._makeOne() result = deriver(view) self.assertTrue(result.__wraps__ is view) self.assertFalse(hasattr(result, '__call_permissive__')) self.assertEqual(view(None, None), 'OK') def test_as_function_requestonly(self): response = DummyResponse() def view(request): return response deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) self.assertEqual(result(None, None), response) def test_as_newstyle_class_context_and_request(self): response = DummyResponse() class view(object): def __init__(self, context, request): pass def __call__(self): return response deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() self.assertEqual(result(None, request), response) self.assertEqual(request.__view__.__class__, view) def test_as_newstyle_class_requestonly(self): response = DummyResponse() class view(object): def __init__(self, context, request): pass def __call__(self): return response deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() self.assertEqual(result(None, request), response) self.assertEqual(request.__view__.__class__, view) def test_as_oldstyle_class_context_and_request(self): response = DummyResponse() class view: def __init__(self, context, request): pass def __call__(self): return response deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() self.assertEqual(result(None, request), response) self.assertEqual(request.__view__.__class__, view) def test_as_oldstyle_class_requestonly(self): response = DummyResponse() class view: def __init__(self, context, request): pass def __call__(self): return response deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() self.assertEqual(result(None, request), response) self.assertEqual(request.__view__.__class__, view) def test_as_instance_context_and_request(self): response = DummyResponse() class View: def __call__(self, context, request): return response view = View() deriver = self._makeOne() result = deriver(view) self.assertTrue(result.__wraps__ is view) self.assertFalse(hasattr(result, '__call_permissive__')) self.assertEqual(result(None, None), response) def test_as_instance_requestonly(self): response = DummyResponse() class View: def __call__(self, request): return response view = View() deriver = self._makeOne() result = deriver(view) self.assertFalse(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertTrue('test_views' in result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) self.assertEqual(result(None, None), response) def test_with_debug_authorization_no_authpol(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) logger = self._registerLogger() deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): Allowed " "(no authorization policy in use)") def test_with_debug_authorization_authn_policy_no_authz_policy(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict(debug_authorization=True) from pyramid.interfaces import IAuthenticationPolicy policy = DummySecurityPolicy(False) self.config.registry.registerUtility(policy, IAuthenticationPolicy) logger = self._registerLogger() deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): Allowed " "(no authorization policy in use)") def test_with_debug_authorization_authz_policy_no_authn_policy(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict(debug_authorization=True) from pyramid.interfaces import IAuthorizationPolicy policy = DummySecurityPolicy(False) self.config.registry.registerUtility(policy, IAuthorizationPolicy) logger = self._registerLogger() deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): Allowed " "(no authorization policy in use)") def test_with_debug_authorization_no_permission(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) self._registerSecurityPolicy(True) logger = self._registerLogger() deriver = self._makeOne() result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): Allowed (" "no permission registered)") def test_debug_auth_permission_authpol_permitted(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) logger = self._registerLogger() self._registerSecurityPolicy(True) deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertEqual(result.__call_permissive__.__wraps__, view) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): True") def test_debug_auth_permission_authpol_permitted_no_request(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) logger = self._registerLogger() self._registerSecurityPolicy(True) deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertEqual(result.__call_permissive__.__wraps__, view) self.assertEqual(result(None, None), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url None (view name " "None against context None): True") def test_debug_auth_permission_authpol_denied(self): from pyramid.httpexceptions import HTTPForbidden response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) logger = self._registerLogger() self._registerSecurityPolicy(False) deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertEqual(result.__call_permissive__.__wraps__, view) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertRaises(HTTPForbidden, result, None, request) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): False") def test_debug_auth_permission_authpol_denied2(self): view = lambda *arg: 'OK' self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) self._registerLogger() self._registerSecurityPolicy(False) deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' permitted = result.__permitted__(None, None) self.assertEqual(permitted, False) def test_debug_auth_permission_authpol_overridden(self): from pyramid.security import NO_PERMISSION_REQUIRED response = DummyResponse() view = lambda *arg: response self.config.registry.settings = dict( debug_authorization=True, reload_templates=True) logger = self._registerLogger() self._registerSecurityPolicy(False) deriver = self._makeOne(permission=NO_PERMISSION_REQUIRED) result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " "'view_name' against context None): " "Allowed (NO_PERMISSION_REQUIRED)") def test_secured_view_authn_policy_no_authz_policy(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = {} from pyramid.interfaces import IAuthenticationPolicy policy = DummySecurityPolicy(False) self.config.registry.registerUtility(policy, IAuthenticationPolicy) deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) def test_secured_view_authz_policy_no_authn_policy(self): response = DummyResponse() view = lambda *arg: response self.config.registry.settings = {} from pyramid.interfaces import IAuthorizationPolicy policy = DummySecurityPolicy(False) self.config.registry.registerUtility(policy, IAuthorizationPolicy) deriver = self._makeOne(permission='view') result = deriver(view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) self.assertFalse(hasattr(result, '__call_permissive__')) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' self.assertEqual(result(None, request), response) def test_secured_view_raises_forbidden_no_name(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy from pyramid.httpexceptions import HTTPForbidden response = DummyResponse() view = lambda *arg: response self.config.registry.settings = {} policy = DummySecurityPolicy(False) self.config.registry.registerUtility(policy, IAuthenticationPolicy) self.config.registry.registerUtility(policy, IAuthorizationPolicy) deriver = self._makeOne(permission='view') result = deriver(view) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' try: result(None, request) except HTTPForbidden as e: self.assertEqual(e.message, 'Unauthorized: failed permission check') else: # pragma: no cover raise AssertionError def test_secured_view_raises_forbidden_with_name(self): from pyramid.interfaces import IAuthenticationPolicy from pyramid.interfaces import IAuthorizationPolicy from pyramid.httpexceptions import HTTPForbidden def myview(request): pass self.config.registry.settings = {} policy = DummySecurityPolicy(False) self.config.registry.registerUtility(policy, IAuthenticationPolicy) self.config.registry.registerUtility(policy, IAuthorizationPolicy) deriver = self._makeOne(permission='view') result = deriver(myview) request = self._makeRequest() request.view_name = 'view_name' request.url = 'url' try: result(None, request) except HTTPForbidden as e: self.assertEqual(e.message, 'Unauthorized: myview failed permission check') else: # pragma: no cover raise AssertionError def test_predicate_mismatch_view_has_no_name(self): from pyramid.exceptions import PredicateMismatch response = DummyResponse() view = lambda *arg: response def predicate1(context, request): return False predicate1.text = lambda *arg: 'text' deriver = self._makeOne(predicates=[predicate1]) result = deriver(view) request = self._makeRequest() request.method = 'POST' try: result(None, None) except PredicateMismatch as e: self.assertEqual(e.detail, 'predicate mismatch for view (text)') else: # pragma: no cover raise AssertionError def test_predicate_mismatch_view_has_name(self): from pyramid.exceptions import PredicateMismatch def myview(request): pass def predicate1(context, request): return False predicate1.text = lambda *arg: 'text' deriver = self._makeOne(predicates=[predicate1]) result = deriver(myview) request = self._makeRequest() request.method = 'POST' try: result(None, None) except PredicateMismatch as e: self.assertEqual(e.detail, 'predicate mismatch for view myview (text)') else: # pragma: no cover raise AssertionError def test_predicate_mismatch_exception_has_text_in_detail(self): from pyramid.exceptions import PredicateMismatch def myview(request): pass def predicate1(context, request): return True predicate1.text = lambda *arg: 'pred1' def predicate2(context, request): return False predicate2.text = lambda *arg: 'pred2' deriver = self._makeOne(predicates=[predicate1, predicate2]) result = deriver(myview) request = self._makeRequest() request.method = 'POST' try: result(None, None) except PredicateMismatch as e: self.assertEqual(e.detail, 'predicate mismatch for view myview (pred2)') else: # pragma: no cover raise AssertionError def test_with_predicates_all(self): response = DummyResponse() view = lambda *arg: response predicates = [] def predicate1(context, request): predicates.append(True) return True def predicate2(context, request): predicates.append(True) return True deriver = self._makeOne(predicates=[predicate1, predicate2]) result = deriver(view) request = self._makeRequest() request.method = 'POST' next = result(None, None) self.assertEqual(next, response) self.assertEqual(predicates, [True, True]) def test_with_predicates_checker(self): view = lambda *arg: 'OK' predicates = [] def predicate1(context, request): predicates.append(True) return True def predicate2(context, request): predicates.append(True) return True deriver = self._makeOne(predicates=[predicate1, predicate2]) result = deriver(view) request = self._makeRequest() request.method = 'POST' next = result.__predicated__(None, None) self.assertEqual(next, True) self.assertEqual(predicates, [True, True]) def test_with_predicates_notall(self): from pyramid.httpexceptions import HTTPNotFound view = lambda *arg: 'OK' predicates = [] def predicate1(context, request): predicates.append(True) return True predicate1.text = lambda *arg: 'text' def predicate2(context, request): predicates.append(True) return False predicate2.text = lambda *arg: 'text' deriver = self._makeOne(predicates=[predicate1, predicate2]) result = deriver(view) request = self._makeRequest() request.method = 'POST' self.assertRaises(HTTPNotFound, result, None, None) self.assertEqual(predicates, [True, True]) def test_with_wrapper_viewname(self): from pyramid.response import Response from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier inner_response = Response('OK') def inner_view(context, request): return inner_response def outer_view(context, request): self.assertEqual(request.wrapped_response, inner_response) self.assertEqual(request.wrapped_body, inner_response.body) self.assertEqual(request.wrapped_view.__original_view__, inner_view) return Response(b'outer ' + request.wrapped_body) self.config.registry.registerAdapter( outer_view, (IViewClassifier, None, None), IView, 'owrap') deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap') result = deriver(inner_view) self.assertFalse(result is inner_view) self.assertEqual(inner_view.__module__, result.__module__) self.assertEqual(inner_view.__doc__, result.__doc__) request = self._makeRequest() response = result(None, request) self.assertEqual(response.body, b'outer OK') def test_with_wrapper_viewname_notfound(self): from pyramid.response import Response inner_response = Response('OK') def inner_view(context, request): return inner_response deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap') wrapped = deriver(inner_view) request = self._makeRequest() self.assertRaises(ValueError, wrapped, None, request) def test_as_newstyle_class_context_and_request_attr_and_renderer(self): response = DummyResponse() class renderer(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, {'a':'1'}) self.assertEqual(view_inst.__class__, View) self.assertEqual(ctx, context) return response class View(object): def __init__(self, context, request): pass def index(self): return {'a':'1'} deriver = self._makeOne(renderer=renderer(), attr='index') result = deriver(View) self.assertFalse(result is View) self.assertEqual(result.__module__, View.__module__) self.assertEqual(result.__doc__, View.__doc__) self.assertEqual(result.__name__, View.__name__) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_as_newstyle_class_requestonly_attr_and_renderer(self): response = DummyResponse() class renderer(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, {'a':'1'}) self.assertEqual(view_inst.__class__, View) self.assertEqual(ctx, context) return response class View(object): def __init__(self, request): pass def index(self): return {'a':'1'} deriver = self._makeOne(renderer=renderer(), attr='index') result = deriver(View) self.assertFalse(result is View) self.assertEqual(result.__module__, View.__module__) self.assertEqual(result.__doc__, View.__doc__) self.assertEqual(result.__name__, View.__name__) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_as_oldstyle_cls_context_request_attr_and_renderer(self): response = DummyResponse() class renderer(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, {'a':'1'}) self.assertEqual(view_inst.__class__, View) self.assertEqual(ctx, context) return response class View: def __init__(self, context, request): pass def index(self): return {'a':'1'} deriver = self._makeOne(renderer=renderer(), attr='index') result = deriver(View) self.assertFalse(result is View) self.assertEqual(result.__module__, View.__module__) self.assertEqual(result.__doc__, View.__doc__) self.assertEqual(result.__name__, View.__name__) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_as_oldstyle_cls_requestonly_attr_and_renderer(self): response = DummyResponse() class renderer(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, {'a':'1'}) self.assertEqual(view_inst.__class__, View) self.assertEqual(ctx, context) return response class View: def __init__(self, request): pass def index(self): return {'a':'1'} deriver = self._makeOne(renderer=renderer(), attr='index') result = deriver(View) self.assertFalse(result is View) self.assertEqual(result.__module__, View.__module__) self.assertEqual(result.__doc__, View.__doc__) self.assertEqual(result.__name__, View.__name__) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_as_instance_context_and_request_attr_and_renderer(self): response = DummyResponse() class renderer(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, {'a':'1'}) self.assertEqual(view_inst, view) self.assertEqual(ctx, context) return response class View: def index(self, context, request): return {'a':'1'} deriver = self._makeOne(renderer=renderer(), attr='index') view = View() result = deriver(view) self.assertFalse(result is view) self.assertEqual(result.__module__, view.__module__) self.assertEqual(result.__doc__, view.__doc__) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_as_instance_requestonly_attr_and_renderer(self): response = DummyResponse() class renderer(object): def render_view(inself, req, resp, view_inst, ctx): self.assertEqual(req, request) self.assertEqual(resp, {'a':'1'}) self.assertEqual(view_inst, view) self.assertEqual(ctx, context) return response class View: def index(self, request): return {'a':'1'} deriver = self._makeOne(renderer=renderer(), attr='index') view = View() result = deriver(view) self.assertFalse(result is view) self.assertEqual(result.__module__, view.__module__) self.assertEqual(result.__doc__, view.__doc__) request = self._makeRequest() context = testing.DummyResource() self.assertEqual(result(context, request), response) def test_with_view_mapper_config_specified(self): response = DummyResponse() class mapper(object): def __init__(self, **kw): self.kw = kw def __call__(self, view): def wrapped(context, request): return response return wrapped def view(context, request): return 'NOTOK' deriver = self._makeOne(mapper=mapper) result = deriver(view) self.assertFalse(result.__wraps__ is view) self.assertEqual(result(None, None), response) def test_with_view_mapper_view_specified(self): from pyramid.response import Response response = Response() def mapper(**kw): def inner(view): def superinner(context, request): self.assertEqual(request, None) return response return superinner return inner def view(context, request): return 'NOTOK' view.__view_mapper__ = mapper deriver = self._makeOne() result = deriver(view) self.assertFalse(result.__wraps__ is view) self.assertEqual(result(None, None), response) def test_with_view_mapper_default_mapper_specified(self): from pyramid.response import Response response = Response() def mapper(**kw): def inner(view): def superinner(context, request): self.assertEqual(request, None) return response return superinner return inner self.config.set_view_mapper(mapper) def view(context, request): return 'NOTOK' deriver = self._makeOne() result = deriver(view) self.assertFalse(result.__wraps__ is view) self.assertEqual(result(None, None), response) def test_attr_wrapped_view_branching_default_phash(self): from pyramid.config.util import DEFAULT_PHASH def view(context, request): pass deriver = self._makeOne(phash=DEFAULT_PHASH) result = deriver(view) self.assertEqual(result.__wraps__, view) def test_attr_wrapped_view_branching_nondefault_phash(self): def view(context, request): pass deriver = self._makeOne(phash='nondefault') result = deriver(view) self.assertNotEqual(result, view) def test_http_cached_view_integer(self): import datetime from pyramid.response import Response response = Response('OK') def inner_view(context, request): return response deriver = self._makeOne(http_cache=3600) result = deriver(inner_view) self.assertFalse(result is inner_view) self.assertEqual(inner_view.__module__, result.__module__) self.assertEqual(inner_view.__doc__, result.__doc__) request = self._makeRequest() when = datetime.datetime.utcnow() + datetime.timedelta(hours=1) result = result(None, request) self.assertEqual(result, response) headers = dict(result.headerlist) expires = parse_httpdate(headers['Expires']) assert_similar_datetime(expires, when) self.assertEqual(headers['Cache-Control'], 'max-age=3600') def test_http_cached_view_timedelta(self): import datetime from pyramid.response import Response response = Response('OK') def inner_view(context, request): return response deriver = self._makeOne(http_cache=datetime.timedelta(hours=1)) result = deriver(inner_view) self.assertFalse(result is inner_view) self.assertEqual(inner_view.__module__, result.__module__) self.assertEqual(inner_view.__doc__, result.__doc__) request = self._makeRequest() when = datetime.datetime.utcnow() + datetime.timedelta(hours=1) result = result(None, request) self.assertEqual(result, response) headers = dict(result.headerlist) expires = parse_httpdate(headers['Expires']) assert_similar_datetime(expires, when) self.assertEqual(headers['Cache-Control'], 'max-age=3600') def test_http_cached_view_tuple(self): import datetime from pyramid.response import Response response = Response('OK') def inner_view(context, request): return response deriver = self._makeOne(http_cache=(3600, {'public':True})) result = deriver(inner_view) self.assertFalse(result is inner_view) self.assertEqual(inner_view.__module__, result.__module__) self.assertEqual(inner_view.__doc__, result.__doc__) request = self._makeRequest() when = datetime.datetime.utcnow() + datetime.timedelta(hours=1) result = result(None, request) self.assertEqual(result, response) headers = dict(result.headerlist) expires = parse_httpdate(headers['Expires']) assert_similar_datetime(expires, when) self.assertEqual(headers['Cache-Control'], 'max-age=3600, public') def test_http_cached_view_tuple_seconds_None(self): from pyramid.response import Response response = Response('OK') def inner_view(context, request): return response deriver = self._makeOne(http_cache=(None, {'public':True})) result = deriver(inner_view) self.assertFalse(result is inner_view) self.assertEqual(inner_view.__module__, result.__module__) self.assertEqual(inner_view.__doc__, result.__doc__) request = self._makeRequest() result = result(None, request) self.assertEqual(result, response) headers = dict(result.headerlist) self.assertFalse('Expires' in headers) self.assertEqual(headers['Cache-Control'], 'public') def test_http_cached_view_prevent_auto_set(self): from pyramid.response import Response response = Response() response.cache_control.prevent_auto = True def inner_view(context, request): return response deriver = self._makeOne(http_cache=3600) result = deriver(inner_view) request = self._makeRequest() result = result(None, request) self.assertEqual(result, response) # doesn't blow up headers = dict(result.headerlist) self.assertFalse('Expires' in headers) self.assertFalse('Cache-Control' in headers) def test_http_cached_prevent_http_cache_in_settings(self): self.config.registry.settings['prevent_http_cache'] = True from pyramid.response import Response response = Response() def inner_view(context, request): return response deriver = self._makeOne(http_cache=3600) result = deriver(inner_view) request = self._makeRequest() result = result(None, request) self.assertEqual(result, response) headers = dict(result.headerlist) self.assertFalse('Expires' in headers) self.assertFalse('Cache-Control' in headers) def test_http_cached_view_bad_tuple(self): deriver = self._makeOne(http_cache=(None,)) def view(request): pass self.assertRaises(ConfigurationError, deriver, view) class TestDefaultViewMapper(unittest.TestCase): def setUp(self): self.config = testing.setUp() self.registry = self.config.registry def tearDown(self): del self.registry testing.tearDown() def _makeOne(self, **kw): from pyramid.config.views import DefaultViewMapper kw['registry'] = self.registry return DefaultViewMapper(**kw) def _makeRequest(self): request = DummyRequest() request.registry = self.registry return request def test_view_as_function_context_and_request(self): def view(context, request): return 'OK' mapper = self._makeOne() result = mapper(view) self.assertTrue(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test__view_as_function_with_attr(self): def view(context, request): """ """ mapper = self._makeOne(attr='__name__') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertRaises(TypeError, result, None, request) def test_view_as_function_requestonly(self): def view(request): return 'OK' mapper = self._makeOne() result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_function_requestonly_with_attr(self): def view(request): """ """ mapper = self._makeOne(attr='__name__') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertRaises(TypeError, result, None, request) def test_view_as_newstyle_class_context_and_request(self): class view(object): def __init__(self, context, request): pass def __call__(self): return 'OK' mapper = self._makeOne() result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_newstyle_class_context_and_request_with_attr(self): class view(object): def __init__(self, context, request): pass def index(self): return 'OK' mapper = self._makeOne(attr='index') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_newstyle_class_requestonly(self): class view(object): def __init__(self, request): pass def __call__(self): return 'OK' mapper = self._makeOne() result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_newstyle_class_requestonly_with_attr(self): class view(object): def __init__(self, request): pass def index(self): return 'OK' mapper = self._makeOne(attr='index') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_oldstyle_class_context_and_request(self): class view: def __init__(self, context, request): pass def __call__(self): return 'OK' mapper = self._makeOne() result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_oldstyle_class_context_and_request_with_attr(self): class view: def __init__(self, context, request): pass def index(self): return 'OK' mapper = self._makeOne(attr='index') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_oldstyle_class_requestonly(self): class view: def __init__(self, request): pass def __call__(self): return 'OK' mapper = self._makeOne() result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_oldstyle_class_requestonly_with_attr(self): class view: def __init__(self, request): pass def index(self): return 'OK' mapper = self._makeOne(attr='index') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_instance_context_and_request(self): class View: def __call__(self, context, request): return 'OK' view = View() mapper = self._makeOne() result = mapper(view) self.assertTrue(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_instance_context_and_request_and_attr(self): class View: def index(self, context, request): return 'OK' view = View() mapper = self._makeOne(attr='index') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_instance_requestonly(self): class View: def __call__(self, request): return 'OK' view = View() mapper = self._makeOne() result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') def test_view_as_instance_requestonly_with_attr(self): class View: def index(self, request): return 'OK' view = View() mapper = self._makeOne(attr='index') result = mapper(view) self.assertFalse(result is view) request = self._makeRequest() self.assertEqual(result(None, request), 'OK') class Test_preserve_view_attrs(unittest.TestCase): def _callFUT(self, view, wrapped_view): from pyramid.config.views import preserve_view_attrs return preserve_view_attrs(view, wrapped_view) def test_it_same(self): def view(context, request): """ """ result = self._callFUT(view, view) self.assertTrue(result is view) def test_it_view_is_None(self): def view(context, request): """ """ result = self._callFUT(None, view) self.assertTrue(result is view) def test_it_different_with_existing_original_view(self): def view1(context, request): pass view1.__original_view__ = 'abc' def view2(context, request): pass result = self._callFUT(view1, view2) self.assertEqual(result.__original_view__, 'abc') self.assertFalse(result is view1) def test_it_different(self): class DummyView1: """ 1 """ __name__ = '1' __module__ = '1' def __call__(self, context, request): """ """ def __call_permissive__(self, context, request): """ """ def __predicated__(self, context, request): """ """ def __permitted__(self, context, request): """ """ class DummyView2: """ 2 """ __name__ = '2' __module__ = '2' def __call__(self, context, request): """ """ def __call_permissive__(self, context, request): """ """ def __predicated__(self, context, request): """ """ def __permitted__(self, context, request): """ """ view1 = DummyView1() view2 = DummyView2() result = self._callFUT(view2, view1) self.assertEqual(result, view1) self.assertTrue(view1.__original_view__ is view2) self.assertTrue(view1.__doc__ is view2.__doc__) self.assertTrue(view1.__module__ is view2.__module__) self.assertTrue(view1.__name__ is view2.__name__) self.assertTrue(getattr(view1.__call_permissive__, im_func) is getattr(view2.__call_permissive__, im_func)) self.assertTrue(getattr(view1.__permitted__, im_func) is getattr(view2.__permitted__, im_func)) self.assertTrue(getattr(view1.__predicated__, im_func) is getattr(view2.__predicated__, im_func)) class TestStaticURLInfo(unittest.TestCase): def _getTargetClass(self): from pyramid.config.views import StaticURLInfo return StaticURLInfo def _makeOne(self): return self._getTargetClass()() def _makeConfig(self, registrations=None): config = DummyConfig() registry = DummyRegistry() if registrations is not None: registry._static_url_registrations = registrations config.registry = registry return config def _makeRequest(self): request = DummyRequest() request.registry = DummyRegistry() return request def _assertRegistrations(self, config, expected): self.assertEqual(config.registry._static_url_registrations, expected) def test_verifyClass(self): from pyramid.interfaces import IStaticURLInfo from zope.interface.verify import verifyClass verifyClass(IStaticURLInfo, self._getTargetClass()) def test_verifyObject(self): from pyramid.interfaces import IStaticURLInfo from zope.interface.verify import verifyObject verifyObject(IStaticURLInfo, self._makeOne()) def test_generate_missing(self): inst = self._makeOne() request = self._makeRequest() self.assertRaises(ValueError, inst.generate, 'path', request) def test_generate_registration_miss(self): inst = self._makeOne() registrations = [(None, 'spec', 'route_name'), ('http://example.com/foo/', 'package:path/', None)] inst._get_registrations = lambda *x: registrations request = self._makeRequest() result = inst.generate('package:path/abc', request) self.assertEqual(result, 'http://example.com/foo/abc') def test_generate_registration_no_registry_on_request(self): inst = self._makeOne() registrations = [('http://example.com/foo/', 'package:path/', None)] inst._get_registrations = lambda *x: registrations request = self._makeRequest() del request.registry result = inst.generate('package:path/abc', request) self.assertEqual(result, 'http://example.com/foo/abc') def test_generate_slash_in_name1(self): inst = self._makeOne() registrations = [('http://example.com/foo/', 'package:path/', None)] inst._get_registrations = lambda *x: registrations request = self._makeRequest() result = inst.generate('package:path/abc', request) self.assertEqual(result, 'http://example.com/foo/abc') def test_generate_slash_in_name2(self): inst = self._makeOne() registrations = [('http://example.com/foo/', 'package:path/', None)] inst._get_registrations = lambda *x: registrations request = self._makeRequest() result = inst.generate('package:path/', request) self.assertEqual(result, 'http://example.com/foo/') def test_generate_quoting(self): config = testing.setUp() try: config.add_static_view('images', path='mypkg:templates') inst = self._makeOne() request = testing.DummyRequest() request.registry = config.registry result = inst.generate('mypkg:templates/foo%2Fbar', request) self.assertEqual(result, 'http://example.com/images/foo%252Fbar') finally: testing.tearDown() def test_generate_route_url(self): inst = self._makeOne() registrations = [(None, 'package:path/', '__viewname/')] inst._get_registrations = lambda *x: registrations def route_url(n, **kw): self.assertEqual(n, '__viewname/') self.assertEqual(kw, {'subpath':'abc', 'a':1}) return 'url' request = self._makeRequest() request.route_url = route_url result = inst.generate('package:path/abc', request, a=1) self.assertEqual(result, 'url') def test_generate_url_unquoted_local(self): inst = self._makeOne() registrations = [(None, 'package:path/', '__viewname/')] inst._get_registrations = lambda *x: registrations def route_url(n, **kw): self.assertEqual(n, '__viewname/') self.assertEqual(kw, {'subpath':'abc def', 'a':1}) return 'url' request = self._makeRequest() request.route_url = route_url result = inst.generate('package:path/abc def', request, a=1) self.assertEqual(result, 'url') def test_generate_url_quoted_remote(self): inst = self._makeOne() registrations = [('http://example.com/', 'package:path/', None)] inst._get_registrations = lambda *x: registrations request = self._makeRequest() result = inst.generate('package:path/abc def', request, a=1) self.assertEqual(result, 'http://example.com/abc%20def') def test_add_already_exists(self): inst = self._makeOne() config = self._makeConfig( [('http://example.com/', 'package:path/', None)]) inst.add(config, 'http://example.com', 'anotherpackage:path') expected = [('http://example.com/', 'anotherpackage:path/', None)] self._assertRegistrations(config, expected) def test_add_url_withendslash(self): inst = self._makeOne() config = self._makeConfig() inst.add(config, 'http://example.com/', 'anotherpackage:path') expected = [('http://example.com/', 'anotherpackage:path/', None)] self._assertRegistrations(config, expected) def test_add_url_noendslash(self): inst = self._makeOne() config = self._makeConfig() inst.add(config, 'http://example.com', 'anotherpackage:path') expected = [('http://example.com/', 'anotherpackage:path/', None)] self._assertRegistrations(config, expected) def test_add_viewname(self): from pyramid.security import NO_PERMISSION_REQUIRED from pyramid.static import static_view config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1) expected = [(None, 'anotherpackage:path/', '__view/')] self._assertRegistrations(config, expected) self.assertEqual(config.route_args, ('__view/', 'view/*subpath')) self.assertEqual(config.view_kw['permission'], NO_PERMISSION_REQUIRED) self.assertEqual(config.view_kw['view'].__class__, static_view) def test_add_viewname_with_route_prefix(self): config = self._makeConfig() config.route_prefix = '/abc' inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path',) expected = [(None, 'anotherpackage:path/', '__/abc/view/')] self._assertRegistrations(config, expected) self.assertEqual(config.route_args, ('__/abc/view/', 'view/*subpath')) def test_add_viewname_with_permission(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, permission='abc') self.assertEqual(config.view_kw['permission'], 'abc') def test_add_viewname_with_view_permission(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, view_permission='abc') self.assertEqual(config.view_kw['permission'], 'abc') def test_add_viewname_with_view_context(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, view_context=DummyContext) self.assertEqual(config.view_kw['context'], DummyContext) def test_add_viewname_with_view_for(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, view_for=DummyContext) self.assertEqual(config.view_kw['context'], DummyContext) def test_add_viewname_with_for_(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, for_=DummyContext) self.assertEqual(config.view_kw['context'], DummyContext) def test_add_viewname_with_view_renderer(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, view_renderer='mypackage:templates/index.pt') self.assertEqual(config.view_kw['renderer'], 'mypackage:templates/index.pt') def test_add_viewname_with_renderer(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, renderer='mypackage:templates/index.pt') self.assertEqual(config.view_kw['renderer'], 'mypackage:templates/index.pt') def test_add_viewname_with_view_attr(self): config = self._makeConfig() inst = self._makeOne() inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, view_attr='attr') self.assertEqual(config.view_kw['attr'], 'attr') class Test_view_description(unittest.TestCase): def _callFUT(self, view): from pyramid.config.views import view_description return view_description(view) def test_with_text(self): def view(): pass view.__text__ = 'some text' result = self._callFUT(view) self.assertEqual(result, 'some text') def test_without_text(self): def view(): pass result = self._callFUT(view) self.assertEqual(result, 'function pyramid.tests.test_config.test_views.view') class DummyRegistry: pass class DummyRequest: subpath = () matchdict = None def __init__(self, environ=None): if environ is None: environ = {} self.environ = environ self.params = {} self.cookies = {} class DummyContext: pass from zope.interface import implementer from pyramid.interfaces import IResponse @implementer(IResponse) class DummyResponse(object): pass class DummyAccept(object): def __init__(self, *matches): self.matches = list(matches) def best_match(self, offered): if self.matches: for match in self.matches: if match in offered: self.matches.remove(match) return match def __contains__(self, val): return val in self.matches class DummyLogger: def __init__(self): self.messages = [] def info(self, msg): self.messages.append(msg) warn = info debug = info class DummySecurityPolicy: def __init__(self, permitted=True): self.permitted = permitted def effective_principals(self, request): return [] def permits(self, context, principals, permission): return self.permitted class DummyConfig: route_prefix = '' def add_route(self, *args, **kw): self.route_args = args self.route_kw = kw def add_view(self, *args, **kw): self.view_args = args self.view_kw = kw def action(self, discriminator, callable, introspectables=()): callable() def introspectable(self, *arg): return {} from zope.interface import implementer from pyramid.interfaces import IMultiView @implementer(IMultiView) class DummyMultiView: def __init__(self): self.views = [] self.name = 'name' def add(self, view, order, accept=None, phash=None): self.views.append((view, accept, phash)) def __call__(self, context, request): return 'OK1' def __permitted__(self, context, request): """ """ def parse_httpdate(s): import datetime # cannot use %Z, must use literal GMT; Jython honors timezone # but CPython does not return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT") def assert_similar_datetime(one, two): for attr in ('year', 'month', 'day', 'hour', 'minute'): one_attr = getattr(one, attr) two_attr = getattr(two, attr) if not one_attr == two_attr: # pragma: no cover raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr)) class DummyStaticURLInfo: def __init__(self): self.added = [] def add(self, config, name, spec, **kw): self.added.append((config, name, spec, kw)) class DummyViewDefaultsClass(object): __view_defaults__ = { 'containment':'pyramid.tests.test_config.IDummy' } def __init__(self, request): pass def __call__(self): return 'OK' class DummyPredicate(object): def __init__(self, val, config): self.val = val def text(self): return 'dummy' phash = text pyramid-1.4.5/pyramid/tests/test_path.py0000664000175000017500000004747012203712502017644 0ustar takakitakakiimport unittest import os from pyramid.compat import PY3 here = os.path.abspath(os.path.dirname(__file__)) class TestCallerPath(unittest.TestCase): def tearDown(self): from pyramid.tests import test_path if hasattr(test_path, '__abspath__'): del test_path.__abspath__ def _callFUT(self, path, level=2): from pyramid.path import caller_path return caller_path(path, level) def test_isabs(self): result = self._callFUT('/a/b/c') self.assertEqual(result, '/a/b/c') def test_pkgrelative(self): import os result = self._callFUT('a/b/c') self.assertEqual(result, os.path.join(here, 'a/b/c')) def test_memoization_has_abspath(self): import os from pyramid.tests import test_path test_path.__abspath__ = '/foo/bar' result = self._callFUT('a/b/c') self.assertEqual(result, os.path.join('/foo/bar', 'a/b/c')) def test_memoization_success(self): import os from pyramid.tests import test_path result = self._callFUT('a/b/c') self.assertEqual(result, os.path.join(here, 'a/b/c')) self.assertEqual(test_path.__abspath__, here) class TestCallerModule(unittest.TestCase): def _callFUT(self, *arg, **kw): from pyramid.path import caller_module return caller_module(*arg, **kw) def test_it_level_1(self): from pyramid.tests import test_path result = self._callFUT(1) self.assertEqual(result, test_path) def test_it_level_2(self): from pyramid.tests import test_path result = self._callFUT(2) self.assertEqual(result, test_path) def test_it_level_3(self): from pyramid.tests import test_path result = self._callFUT(3) self.assertNotEqual(result, test_path) def test_it_no___name__(self): class DummyFrame(object): f_globals = {} class DummySys(object): def _getframe(self, level): return DummyFrame() modules = {'__main__':'main'} dummy_sys = DummySys() result = self._callFUT(3, sys=dummy_sys) self.assertEqual(result, 'main') class TestCallerPackage(unittest.TestCase): def _callFUT(self, *arg, **kw): from pyramid.path import caller_package return caller_package(*arg, **kw) def test_it_level_1(self): from pyramid import tests result = self._callFUT(1) self.assertEqual(result, tests) def test_it_level_2(self): from pyramid import tests result = self._callFUT(2) self.assertEqual(result, tests) def test_it_level_3(self): import unittest result = self._callFUT(3) self.assertEqual(result, unittest) def test_it_package(self): import pyramid.tests def dummy_caller_module(*arg): return pyramid.tests result = self._callFUT(1, caller_module=dummy_caller_module) self.assertEqual(result, pyramid.tests) class TestPackagePath(unittest.TestCase): def _callFUT(self, package): from pyramid.path import package_path return package_path(package) def test_it_package(self): from pyramid import tests package = DummyPackageOrModule(tests) result = self._callFUT(package) self.assertEqual(result, package.package_path) def test_it_module(self): from pyramid.tests import test_path module = DummyPackageOrModule(test_path) result = self._callFUT(module) self.assertEqual(result, module.package_path) def test_memoization_success(self): from pyramid.tests import test_path module = DummyPackageOrModule(test_path) self._callFUT(module) self.assertEqual(module.__abspath__, module.package_path) def test_memoization_fail(self): from pyramid.tests import test_path module = DummyPackageOrModule(test_path, raise_exc=TypeError) result = self._callFUT(module) self.assertFalse(hasattr(module, '__abspath__')) self.assertEqual(result, module.package_path) class TestPackageOf(unittest.TestCase): def _callFUT(self, package): from pyramid.path import package_of return package_of(package) def test_it_package(self): from pyramid import tests package = DummyPackageOrModule(tests) result = self._callFUT(package) self.assertEqual(result, tests) def test_it_module(self): import pyramid.tests.test_path from pyramid import tests package = DummyPackageOrModule(pyramid.tests.test_path) result = self._callFUT(package) self.assertEqual(result, tests) class TestPackageName(unittest.TestCase): def _callFUT(self, package): from pyramid.path import package_name return package_name(package) def test_it_package(self): from pyramid import tests package = DummyPackageOrModule(tests) result = self._callFUT(package) self.assertEqual(result, 'pyramid.tests') def test_it_module(self): from pyramid.tests import test_path module = DummyPackageOrModule(test_path) result = self._callFUT(module) self.assertEqual(result, 'pyramid.tests') def test_it_None(self): result = self._callFUT(None) self.assertEqual(result, '__main__') def test_it_main(self): import __main__ result = self._callFUT(__main__) self.assertEqual(result, '__main__') class TestResolver(unittest.TestCase): def _getTargetClass(self): from pyramid.path import Resolver return Resolver def _makeOne(self, package): return self._getTargetClass()(package) def test_get_package_caller_package(self): import pyramid.tests from pyramid.path import CALLER_PACKAGE self.assertEqual(self._makeOne(CALLER_PACKAGE).get_package(), pyramid.tests) def test_get_package_name_caller_package(self): from pyramid.path import CALLER_PACKAGE self.assertEqual(self._makeOne(CALLER_PACKAGE).get_package_name(), 'pyramid.tests') def test_get_package_string(self): import pyramid.tests self.assertEqual(self._makeOne('pyramid.tests').get_package(), pyramid.tests) def test_get_package_name_string(self): self.assertEqual(self._makeOne('pyramid.tests').get_package_name(), 'pyramid.tests') class TestAssetResolver(unittest.TestCase): def _getTargetClass(self): from pyramid.path import AssetResolver return AssetResolver def _makeOne(self, package='pyramid.tests'): return self._getTargetClass()(package) def test_ctor_as_package(self): import sys tests = sys.modules['pyramid.tests'] inst = self._makeOne(tests) self.assertEqual(inst.package, tests) def test_ctor_as_str(self): import sys tests = sys.modules['pyramid.tests'] inst = self._makeOne('pyramid.tests') self.assertEqual(inst.package, tests) def test_resolve_abspath(self): from pyramid.path import FSAssetDescriptor inst = self._makeOne(None) r = inst.resolve(os.path.join(here, 'test_asset.py')) self.assertEqual(r.__class__, FSAssetDescriptor) self.assertTrue(r.exists()) def test_resolve_absspec(self): from pyramid.path import PkgResourcesAssetDescriptor inst = self._makeOne(None) r = inst.resolve('pyramid.tests:test_asset.py') self.assertEqual(r.__class__, PkgResourcesAssetDescriptor) self.assertTrue(r.exists()) def test_resolve_relspec_with_pkg(self): from pyramid.path import PkgResourcesAssetDescriptor inst = self._makeOne('pyramid.tests') r = inst.resolve('test_asset.py') self.assertEqual(r.__class__, PkgResourcesAssetDescriptor) self.assertTrue(r.exists()) def test_resolve_relspec_no_package(self): inst = self._makeOne(None) self.assertRaises(ValueError, inst.resolve, 'test_asset.py') def test_resolve_relspec_caller_package(self): from pyramid.path import PkgResourcesAssetDescriptor from pyramid.path import CALLER_PACKAGE inst = self._makeOne(CALLER_PACKAGE) r = inst.resolve('test_asset.py') self.assertEqual(r.__class__, PkgResourcesAssetDescriptor) self.assertTrue(r.exists()) class TestPkgResourcesAssetDescriptor(unittest.TestCase): def _getTargetClass(self): from pyramid.path import PkgResourcesAssetDescriptor return PkgResourcesAssetDescriptor def _makeOne(self, pkg='pyramid.tests', path='test_asset.py'): return self._getTargetClass()(pkg, path) def test_class_conforms_to_IAssetDescriptor(self): from pyramid.interfaces import IAssetDescriptor from zope.interface.verify import verifyClass verifyClass(IAssetDescriptor, self._getTargetClass()) def test_instance_conforms_to_IAssetDescriptor(self): from pyramid.interfaces import IAssetDescriptor from zope.interface.verify import verifyObject verifyObject(IAssetDescriptor, self._makeOne()) def test_absspec(self): inst = self._makeOne() self.assertEqual(inst.absspec(), 'pyramid.tests:test_asset.py') def test_abspath(self): inst = self._makeOne() self.assertEqual(inst.abspath(), os.path.join(here, 'test_asset.py')) def test_stream(self): inst = self._makeOne() inst.pkg_resources = DummyPkgResource() inst.pkg_resources.resource_stream = lambda x, y: '%s:%s' % (x, y) s = inst.stream() self.assertEqual(s, '%s:%s' % ('pyramid.tests', 'test_asset.py')) def test_isdir(self): inst = self._makeOne() inst.pkg_resources = DummyPkgResource() inst.pkg_resources.resource_isdir = lambda x, y: '%s:%s' % (x, y) self.assertEqual(inst.isdir(), '%s:%s' % ('pyramid.tests', 'test_asset.py')) def test_listdir(self): inst = self._makeOne() inst.pkg_resources = DummyPkgResource() inst.pkg_resources.resource_listdir = lambda x, y: '%s:%s' % (x, y) self.assertEqual(inst.listdir(), '%s:%s' % ('pyramid.tests', 'test_asset.py')) def test_exists(self): inst = self._makeOne() inst.pkg_resources = DummyPkgResource() inst.pkg_resources.resource_exists = lambda x, y: '%s:%s' % (x, y) self.assertEqual(inst.exists(), '%s:%s' % ('pyramid.tests', 'test_asset.py')) class TestFSAssetDescriptor(unittest.TestCase): def _getTargetClass(self): from pyramid.path import FSAssetDescriptor return FSAssetDescriptor def _makeOne(self, path=os.path.join(here, 'test_asset.py')): return self._getTargetClass()(path) def test_class_conforms_to_IAssetDescriptor(self): from pyramid.interfaces import IAssetDescriptor from zope.interface.verify import verifyClass verifyClass(IAssetDescriptor, self._getTargetClass()) def test_instance_conforms_to_IAssetDescriptor(self): from pyramid.interfaces import IAssetDescriptor from zope.interface.verify import verifyObject verifyObject(IAssetDescriptor, self._makeOne()) def test_absspec(self): inst = self._makeOne() self.assertRaises(NotImplementedError, inst.absspec) def test_abspath(self): inst = self._makeOne() self.assertEqual(inst.abspath(), os.path.join(here, 'test_asset.py')) def test_stream(self): inst = self._makeOne() s = inst.stream() val = s.read() s.close() self.assertTrue(b'asset' in val) def test_isdir_False(self): inst = self._makeOne() self.assertFalse(inst.isdir()) def test_isdir_True(self): inst = self._makeOne(here) self.assertTrue(inst.isdir()) def test_listdir(self): inst = self._makeOne(here) self.assertTrue(inst.listdir()) def test_exists(self): inst = self._makeOne() self.assertTrue(inst.exists()) class TestDottedNameResolver(unittest.TestCase): def _makeOne(self, package=None): from pyramid.path import DottedNameResolver return DottedNameResolver(package) def config_exc(self, func, *arg, **kw): try: func(*arg, **kw) except ValueError as e: return e else: raise AssertionError('Invalid not raised') # pragma: no cover def test_zope_dottedname_style_resolve_builtin(self): typ = self._makeOne() if PY3: # pragma: no cover result = typ._zope_dottedname_style('builtins.str', None) else: result = typ._zope_dottedname_style('__builtin__.str', None) self.assertEqual(result, str) def test_zope_dottedname_style_resolve_absolute(self): typ = self._makeOne() result = typ._zope_dottedname_style( 'pyramid.tests.test_path.TestDottedNameResolver', None) self.assertEqual(result, self.__class__) def test_zope_dottedname_style_irrresolveable_absolute(self): typ = self._makeOne() self.assertRaises(ImportError, typ._zope_dottedname_style, 'pyramid.test_path.nonexisting_name', None) def test__zope_dottedname_style_resolve_relative(self): import pyramid.tests typ = self._makeOne() result = typ._zope_dottedname_style( '.test_path.TestDottedNameResolver', pyramid.tests) self.assertEqual(result, self.__class__) def test__zope_dottedname_style_resolve_relative_leading_dots(self): import pyramid.tests.test_path typ = self._makeOne() result = typ._zope_dottedname_style( '..tests.test_path.TestDottedNameResolver', pyramid.tests) self.assertEqual(result, self.__class__) def test__zope_dottedname_style_resolve_relative_is_dot(self): import pyramid.tests typ = self._makeOne() result = typ._zope_dottedname_style('.', pyramid.tests) self.assertEqual(result, pyramid.tests) def test__zope_dottedname_style_irresolveable_relative_is_dot(self): typ = self._makeOne() e = self.config_exc(typ._zope_dottedname_style, '.', None) self.assertEqual( e.args[0], "relative name '.' irresolveable without package") def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self): typ = self._makeOne() e = self.config_exc(typ._zope_dottedname_style, '.whatever', None) self.assertEqual( e.args[0], "relative name '.whatever' irresolveable without package") def test_zope_dottedname_style_irrresolveable_relative(self): import pyramid.tests typ = self._makeOne() self.assertRaises(ImportError, typ._zope_dottedname_style, '.notexisting', pyramid.tests) def test__zope_dottedname_style_resolveable_relative(self): import pyramid typ = self._makeOne() result = typ._zope_dottedname_style('.tests', pyramid) from pyramid import tests self.assertEqual(result, tests) def test__zope_dottedname_style_irresolveable_absolute(self): typ = self._makeOne() self.assertRaises( ImportError, typ._zope_dottedname_style, 'pyramid.fudge.bar', None) def test__zope_dottedname_style_resolveable_absolute(self): typ = self._makeOne() result = typ._zope_dottedname_style( 'pyramid.tests.test_path.TestDottedNameResolver', None) self.assertEqual(result, self.__class__) def test__pkg_resources_style_resolve_absolute(self): typ = self._makeOne() result = typ._pkg_resources_style( 'pyramid.tests.test_path:TestDottedNameResolver', None) self.assertEqual(result, self.__class__) def test__pkg_resources_style_irrresolveable_absolute(self): typ = self._makeOne() self.assertRaises(ImportError, typ._pkg_resources_style, 'pyramid.tests:nonexisting', None) def test__pkg_resources_style_resolve_relative(self): import pyramid.tests typ = self._makeOne() result = typ._pkg_resources_style( '.test_path:TestDottedNameResolver', pyramid.tests) self.assertEqual(result, self.__class__) def test__pkg_resources_style_resolve_relative_is_dot(self): import pyramid.tests typ = self._makeOne() result = typ._pkg_resources_style('.', pyramid.tests) self.assertEqual(result, pyramid.tests) def test__pkg_resources_style_resolve_relative_nocurrentpackage(self): typ = self._makeOne() self.assertRaises(ValueError, typ._pkg_resources_style, '.whatever', None) def test__pkg_resources_style_irrresolveable_relative(self): import pyramid typ = self._makeOne() self.assertRaises(ImportError, typ._pkg_resources_style, ':notexisting', pyramid) def test_resolve_not_a_string(self): typ = self._makeOne() e = self.config_exc(typ.resolve, None) self.assertEqual(e.args[0], 'None is not a string') def test_resolve_using_pkgresources_style(self): typ = self._makeOne() result = typ.resolve( 'pyramid.tests.test_path:TestDottedNameResolver') self.assertEqual(result, self.__class__) def test_resolve_using_zope_dottedname_style(self): typ = self._makeOne() result = typ.resolve( 'pyramid.tests.test_path:TestDottedNameResolver') self.assertEqual(result, self.__class__) def test_resolve_missing_raises(self): typ = self._makeOne() self.assertRaises(ImportError, typ.resolve, 'cant.be.found') def test_resolve_caller_package(self): from pyramid.path import CALLER_PACKAGE typ = self._makeOne(CALLER_PACKAGE) self.assertEqual(typ.resolve('.test_path.TestDottedNameResolver'), self.__class__) def test_maybe_resolve_caller_package(self): from pyramid.path import CALLER_PACKAGE typ = self._makeOne(CALLER_PACKAGE) self.assertEqual(typ.maybe_resolve('.test_path.TestDottedNameResolver'), self.__class__) def test_ctor_string_module_resolveable(self): import pyramid.tests typ = self._makeOne('pyramid.tests.test_path') self.assertEqual(typ.package, pyramid.tests) def test_ctor_string_package_resolveable(self): import pyramid.tests typ = self._makeOne('pyramid.tests') self.assertEqual(typ.package, pyramid.tests) def test_ctor_string_irresolveable(self): self.assertRaises(ValueError, self._makeOne, 'cant.be.found') def test_ctor_module(self): import pyramid.tests import pyramid.tests.test_path typ = self._makeOne(pyramid.tests.test_path) self.assertEqual(typ.package, pyramid.tests) def test_ctor_package(self): import pyramid.tests typ = self._makeOne(pyramid.tests) self.assertEqual(typ.package, pyramid.tests) def test_ctor_None(self): typ = self._makeOne(None) self.assertEqual(typ.package, None) class DummyPkgResource(object): pass class DummyPackageOrModule: def __init__(self, real_package_or_module, raise_exc=None): self.__dict__['raise_exc'] = raise_exc self.__dict__['__name__'] = real_package_or_module.__name__ import os self.__dict__['package_path'] = os.path.dirname( os.path.abspath(real_package_or_module.__file__)) self.__dict__['__file__'] = real_package_or_module.__file__ def __setattr__(self, key, val): if self.raise_exc is not None: raise self.raise_exc self.__dict__[key] = val pyramid-1.4.5/pyramid/tests/test_integration.py0000664000175000017500000006021012210154301021211 0ustar takakitakaki# -*- coding: utf-8 -*- import datetime import locale import os import unittest from pyramid.wsgi import wsgiapp from pyramid.view import view_config from pyramid.static import static_view from pyramid.compat import ( text_, url_quote, ) from zope.interface import Interface # 5 years from now (more or less) fiveyrsfuture = datetime.datetime.utcnow() + datetime.timedelta(5*365) defaultlocale = locale.getdefaultlocale()[1] class INothing(Interface): pass @view_config(for_=INothing) @wsgiapp def wsgiapptest(environ, start_response): """ """ return '123' class WGSIAppPlusViewConfigTests(unittest.TestCase): def test_it(self): from venusian import ATTACH_ATTR import types self.assertTrue(getattr(wsgiapptest, ATTACH_ATTR)) self.assertTrue(type(wsgiapptest) is types.FunctionType) context = DummyContext() request = DummyRequest() result = wsgiapptest(context, request) self.assertEqual(result, '123') def test_scanned(self): from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.config import Configurator from pyramid.tests import test_integration config = Configurator() config.scan(test_integration) config.commit() reg = config.registry view = reg.adapters.lookup( (IViewClassifier, IRequest, INothing), IView, name='') self.assertEqual(view.__original_view__, wsgiapptest) class IntegrationBase(object): root_factory = None package = None def setUp(self): from pyramid.config import Configurator config = Configurator(root_factory=self.root_factory, package=self.package) config.include(self.package) app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) self.config = config def tearDown(self): self.config.end() here = os.path.dirname(__file__) class TestStaticAppBase(IntegrationBase): def test_basic(self): res = self.testapp.get('/minimal.pt', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/minimal.pt')) def test_hidden(self): res = self.testapp.get('/static/.hiddenfile', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/static/.hiddenfile')) if defaultlocale is not None: # These tests are expected to fail on LANG=C systems due to decode # errors and on non-Linux systems due to git highchar handling # vagaries def test_highchars_in_pathelement(self): path = os.path.join( here, text_('fixtures/static/héhé/index.html', 'utf-8')) pathdir = os.path.dirname(path) body = b'hehe\n' try: os.makedirs(pathdir) with open(path, 'wb') as fp: fp.write(body) url = url_quote('/static/héhé/index.html') res = self.testapp.get(url, status=200) self.assertEqual(res.body, body) finally: os.unlink(path) os.rmdir(pathdir) def test_highchars_in_filename(self): path = os.path.join( here, text_('fixtures/static/héhé.html', 'utf-8')) body = b'hehe file\n' with open(path, 'wb') as fp: fp.write(body) try: url = url_quote('/static/héhé.html') res = self.testapp.get(url, status=200) self.assertEqual(res.body, body) finally: os.unlink(path) def test_not_modified(self): self.testapp.extra_environ = { 'HTTP_IF_MODIFIED_SINCE':httpdate(fiveyrsfuture)} res = self.testapp.get('/minimal.pt', status=304) self.assertEqual(res.body, b'') def test_file_in_subdir(self): fn = os.path.join(here, 'fixtures/static/index.html') res = self.testapp.get('/static/index.html', status=200) _assertBody(res.body, fn) def test_directory_noslash_redir(self): res = self.testapp.get('/static', status=301) self.assertEqual(res.headers['Location'], 'http://localhost/static/') def test_directory_noslash_redir_preserves_qs(self): res = self.testapp.get('/static?a=1&b=2', status=301) self.assertEqual(res.headers['Location'], 'http://localhost/static/?a=1&b=2') def test_directory_noslash_redir_with_scriptname(self): self.testapp.extra_environ = {'SCRIPT_NAME':'/script_name'} res = self.testapp.get('/static', status=301) self.assertEqual(res.headers['Location'], 'http://localhost/script_name/static/') def test_directory_withslash(self): fn = os.path.join(here, 'fixtures/static/index.html') res = self.testapp.get('/static/', status=200) _assertBody(res.body, fn) def test_range_inclusive(self): self.testapp.extra_environ = {'HTTP_RANGE':'bytes=1-2'} res = self.testapp.get('/static/index.html', status=206) self.assertEqual(res.body, b'ht') def test_range_tilend(self): self.testapp.extra_environ = {'HTTP_RANGE':'bytes=-5'} res = self.testapp.get('/static/index.html', status=206) self.assertEqual(res.body, b'html>') def test_range_notbytes(self): self.testapp.extra_environ = {'HTTP_RANGE':'kHz=-5'} res = self.testapp.get('/static/index.html', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/static/index.html')) def test_range_multiple(self): res = self.testapp.get('/static/index.html', [('HTTP_RANGE', 'bytes=10-11,11-12')], status=200) _assertBody(res.body, os.path.join(here, 'fixtures/static/index.html')) def test_range_oob(self): self.testapp.extra_environ = {'HTTP_RANGE':'bytes=1000-1002'} self.testapp.get('/static/index.html', status=416) def test_notfound(self): self.testapp.get('/static/wontbefound.html', status=404) def test_oob_dotdotslash(self): self.testapp.get('/static/../../test_integration.py', status=404) def test_oob_dotdotslash_encoded(self): self.testapp.get('/static/%2E%2E%2F/test_integration.py', status=404) def test_oob_slash(self): self.testapp.get('/%2F/test_integration.py', status=404) class TestEventOnlySubscribers(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.eventonly' def test_sendfoo(self): res = self.testapp.get('/sendfoo', status=200) self.assertEqual(sorted(res.body.split()), [b'foo', b'fooyup']) def test_sendfoobar(self): res = self.testapp.get('/sendfoobar', status=200) self.assertEqual(sorted(res.body.split()), [b'foobar', b'foobar2', b'foobaryup', b'foobaryup2']) class TestStaticAppUsingAbsPath(TestStaticAppBase, unittest.TestCase): package = 'pyramid.tests.pkgs.static_abspath' class TestStaticAppUsingAssetSpec(TestStaticAppBase, unittest.TestCase): package = 'pyramid.tests.pkgs.static_assetspec' class TestStaticAppNoSubpath(unittest.TestCase): staticapp = static_view(os.path.join(here, 'fixtures'), use_subpath=False) def _makeRequest(self, extra): from pyramid.request import Request from io import BytesIO kw = {'PATH_INFO':'', 'SCRIPT_NAME':'', 'SERVER_NAME':'localhost', 'SERVER_PORT':'80', 'REQUEST_METHOD':'GET', 'wsgi.version':(1,0), 'wsgi.url_scheme':'http', 'wsgi.input':BytesIO()} kw.update(extra) request = Request(kw) return request def test_basic(self): request = self._makeRequest({'PATH_INFO':'/minimal.pt'}) context = DummyContext() result = self.staticapp(context, request) self.assertEqual(result.status, '200 OK') _assertBody(result.body, os.path.join(here, 'fixtures/minimal.pt')) class TestStaticAppWithRoutePrefix(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.static_routeprefix' def test_includelevel1(self): res = self.testapp.get('/static/minimal.pt', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/minimal.pt')) def test_includelevel2(self): res = self.testapp.get('/prefix/static/index.html', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/static/index.html')) class TestFixtureApp(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.fixtureapp' def test_another(self): res = self.testapp.get('/another.html', status=200) self.assertEqual(res.body, b'fixture') def test_root(self): res = self.testapp.get('/', status=200) self.assertEqual(res.body, b'fixture') def test_dummyskin(self): self.testapp.get('/dummyskin.html', status=404) def test_error(self): res = self.testapp.get('/error.html', status=200) self.assertEqual(res.body, b'supressed') def test_protected(self): self.testapp.get('/protected.html', status=403) class TestStaticPermApp(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.staticpermapp' root_factory = 'pyramid.tests.pkgs.staticpermapp:RootFactory' def test_allowed(self): result = self.testapp.get('/allowed/index.html', status=200) _assertBody(result.body, os.path.join(here, 'fixtures/static/index.html')) def test_denied_via_acl_global_root_factory(self): self.testapp.extra_environ = {'REMOTE_USER':'bob'} self.testapp.get('/protected/index.html', status=403) def test_allowed_via_acl_global_root_factory(self): self.testapp.extra_environ = {'REMOTE_USER':'fred'} result = self.testapp.get('/protected/index.html', status=200) _assertBody(result.body, os.path.join(here, 'fixtures/static/index.html')) def test_denied_via_acl_local_root_factory(self): self.testapp.extra_environ = {'REMOTE_USER':'fred'} self.testapp.get('/factory_protected/index.html', status=403) def test_allowed_via_acl_local_root_factory(self): self.testapp.extra_environ = {'REMOTE_USER':'bob'} result = self.testapp.get('/factory_protected/index.html', status=200) _assertBody(result.body, os.path.join(here, 'fixtures/static/index.html')) class TestCCBug(IntegrationBase, unittest.TestCase): # "unordered" as reported in IRC by author of # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ package = 'pyramid.tests.pkgs.ccbugapp' def test_rdf(self): res = self.testapp.get('/licenses/1/v1/rdf', status=200) self.assertEqual(res.body, b'rdf') def test_juri(self): res = self.testapp.get('/licenses/1/v1/juri', status=200) self.assertEqual(res.body, b'juri') class TestHybridApp(IntegrationBase, unittest.TestCase): # make sure views registered for a route "win" over views registered # without one, even though the context of the non-route view may # be more specific than the route view. package = 'pyramid.tests.pkgs.hybridapp' def test_root(self): res = self.testapp.get('/', status=200) self.assertEqual(res.body, b'global') def test_abc(self): res = self.testapp.get('/abc', status=200) self.assertEqual(res.body, b'route') def test_def(self): res = self.testapp.get('/def', status=200) self.assertEqual(res.body, b'route2') def test_ghi(self): res = self.testapp.get('/ghi', status=200) self.assertEqual(res.body, b'global') def test_jkl(self): self.testapp.get('/jkl', status=404) def test_mno(self): self.testapp.get('/mno', status=404) def test_pqr_global2(self): res = self.testapp.get('/pqr/global2', status=200) self.assertEqual(res.body, b'global2') def test_error(self): res = self.testapp.get('/error', status=200) self.assertEqual(res.body, b'supressed') def test_error2(self): res = self.testapp.get('/error2', status=200) self.assertEqual(res.body, b'supressed2') def test_error_sub(self): res = self.testapp.get('/error_sub', status=200) self.assertEqual(res.body, b'supressed2') class TestRestBugApp(IntegrationBase, unittest.TestCase): # test bug reported by delijati 2010/2/3 (http://pastebin.com/d4cc15515) package = 'pyramid.tests.pkgs.restbugapp' def test_it(self): res = self.testapp.get('/pet', status=200) self.assertEqual(res.body, b'gotten') class TestForbiddenAppHasResult(IntegrationBase, unittest.TestCase): # test that forbidden exception has ACLDenied result attached package = 'pyramid.tests.pkgs.forbiddenapp' def test_it(self): res = self.testapp.get('/x', status=403) message, result = [x.strip() for x in res.body.split(b'\n')] self.assertTrue(message.endswith(b'failed permission check')) self.assertTrue( result.startswith(b"ACLDenied permission 'private' via ACE " b"'' in ACL " b"'' on context")) self.assertTrue( result.endswith(b"for principals ['system.Everyone']")) class TestViewDecoratorApp(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.viewdecoratorapp' def test_first(self): res = self.testapp.get('/first', status=200) self.assertTrue(b'OK' in res.body) def test_second(self): res = self.testapp.get('/second', status=200) self.assertTrue(b'OK2' in res.body) class TestNotFoundView(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.notfoundview' def test_it(self): res = self.testapp.get('/wontbefound', status=200) self.assertTrue(b'generic_notfound' in res.body) res = self.testapp.get('/bar', status=302) self.assertEqual(res.location, 'http://localhost/bar/') res = self.testapp.get('/bar/', status=200) self.assertTrue(b'OK bar' in res.body) res = self.testapp.get('/foo', status=302) self.assertEqual(res.location, 'http://localhost/foo/') res = self.testapp.get('/foo/', status=200) self.assertTrue(b'OK foo2' in res.body) res = self.testapp.get('/baz', status=200) self.assertTrue(b'baz_notfound' in res.body) class TestForbiddenView(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.forbiddenview' def test_it(self): res = self.testapp.get('/foo', status=200) self.assertTrue(b'foo_forbidden' in res.body) res = self.testapp.get('/bar', status=200) self.assertTrue(b'generic_forbidden' in res.body) class TestViewPermissionBug(IntegrationBase, unittest.TestCase): # view_execution_permitted bug as reported by Shane at http://lists.repoze.org/pipermail/repoze-dev/2010-October/003603.html package = 'pyramid.tests.pkgs.permbugapp' def test_test(self): res = self.testapp.get('/test', status=200) self.assertTrue(b'ACLDenied' in res.body) def test_x(self): self.testapp.get('/x', status=403) class TestDefaultViewPermissionBug(IntegrationBase, unittest.TestCase): # default_view_permission bug as reported by Wiggy at http://lists.repoze.org/pipermail/repoze-dev/2010-October/003602.html package = 'pyramid.tests.pkgs.defpermbugapp' def test_x(self): res = self.testapp.get('/x', status=403) self.assertTrue(b'failed permission check' in res.body) def test_y(self): res = self.testapp.get('/y', status=403) self.assertTrue(b'failed permission check' in res.body) def test_z(self): res = self.testapp.get('/z', status=200) self.assertTrue(b'public' in res.body) from pyramid.tests.pkgs.exceptionviewapp.models import \ AnException, NotAnException excroot = {'anexception':AnException(), 'notanexception':NotAnException()} class TestExceptionViewsApp(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.exceptionviewapp' root_factory = lambda *arg: excroot def test_root(self): res = self.testapp.get('/', status=200) self.assertTrue(b'maybe' in res.body) def test_notanexception(self): res = self.testapp.get('/notanexception', status=200) self.assertTrue(b'no' in res.body) def test_anexception(self): res = self.testapp.get('/anexception', status=200) self.assertTrue(b'yes' in res.body) def test_route_raise_exception(self): res = self.testapp.get('/route_raise_exception', status=200) self.assertTrue(b'yes' in res.body) def test_route_raise_exception2(self): res = self.testapp.get('/route_raise_exception2', status=200) self.assertTrue(b'yes' in res.body) def test_route_raise_exception3(self): res = self.testapp.get('/route_raise_exception3', status=200) self.assertTrue(b'whoa' in res.body) def test_route_raise_exception4(self): res = self.testapp.get('/route_raise_exception4', status=200) self.assertTrue(b'whoa' in res.body) def test_raise_httpexception(self): res = self.testapp.get('/route_raise_httpexception', status=200) self.assertTrue(b'caught' in res.body) class TestConflictApp(unittest.TestCase): package = 'pyramid.tests.pkgs.conflictapp' def _makeConfig(self): from pyramid.config import Configurator config = Configurator() return config def test_autoresolved_view(self): config = self._makeConfig() config.include(self.package) app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) res = self.testapp.get('/') self.assertTrue(b'a view' in res.body) res = self.testapp.get('/route') self.assertTrue(b'route view' in res.body) def test_overridden_autoresolved_view(self): from pyramid.response import Response config = self._makeConfig() config.include(self.package) def thisview(request): return Response('this view') config.add_view(thisview) app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) res = self.testapp.get('/') self.assertTrue(b'this view' in res.body) def test_overridden_route_view(self): from pyramid.response import Response config = self._makeConfig() config.include(self.package) def thisview(request): return Response('this view') config.add_view(thisview, route_name='aroute') app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) res = self.testapp.get('/route') self.assertTrue(b'this view' in res.body) def test_nonoverridden_authorization_policy(self): config = self._makeConfig() config.include(self.package) app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) res = self.testapp.get('/protected', status=403) self.assertTrue(b'403 Forbidden' in res.body) def test_overridden_authorization_policy(self): config = self._makeConfig() config.include(self.package) from pyramid.testing import DummySecurityPolicy config.set_authorization_policy(DummySecurityPolicy('fred')) config.set_authentication_policy(DummySecurityPolicy(permissive=True)) app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) res = self.testapp.get('/protected', status=200) self.assertTrue('protected view' in res) class ImperativeIncludeConfigurationTest(unittest.TestCase): def setUp(self): from pyramid.config import Configurator config = Configurator() from pyramid.tests.pkgs.includeapp1.root import configure configure(config) app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) self.config = config def tearDown(self): self.config.end() def test_root(self): res = self.testapp.get('/', status=200) self.assertTrue(b'root' in res.body) def test_two(self): res = self.testapp.get('/two', status=200) self.assertTrue(b'two' in res.body) def test_three(self): res = self.testapp.get('/three', status=200) self.assertTrue(b'three' in res.body) class SelfScanAppTest(unittest.TestCase): def setUp(self): from pyramid.tests.test_config.pkgs.selfscan import main config = main() app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) self.config = config def tearDown(self): self.config.end() def test_root(self): res = self.testapp.get('/', status=200) self.assertTrue(b'root' in res.body) def test_two(self): res = self.testapp.get('/two', status=200) self.assertTrue(b'two' in res.body) class WSGIApp2AppTest(unittest.TestCase): def setUp(self): from pyramid.tests.pkgs.wsgiapp2app import main config = main() app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) self.config = config def tearDown(self): self.config.end() def test_hello(self): res = self.testapp.get('/hello', status=200) self.assertTrue(b'Hello' in res.body) class SubrequestAppTest(unittest.TestCase): def setUp(self): from pyramid.tests.pkgs.subrequestapp import main config = main() app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) self.config = config def tearDown(self): self.config.end() def test_one(self): res = self.testapp.get('/view_one', status=200) self.assertTrue(b'This came from view_two' in res.body) def test_three(self): res = self.testapp.get('/view_three', status=500) self.assertTrue(b'Bad stuff happened' in res.body) def test_five(self): res = self.testapp.get('/view_five', status=200) self.assertTrue(b'Value error raised' in res.body) class RendererScanAppTest(IntegrationBase, unittest.TestCase): package = 'pyramid.tests.pkgs.rendererscanapp' def test_root(self): res = self.testapp.get('/one', status=200) self.assertTrue(b'One!' in res.body) def test_two(self): res = self.testapp.get('/two', status=200) self.assertTrue(b'Two!' in res.body) def test_rescan(self): self.config.scan('pyramid.tests.pkgs.rendererscanapp') app = self.config.make_wsgi_app() from webtest import TestApp testapp = TestApp(app) res = testapp.get('/one', status=200) self.assertTrue(b'One!' in res.body) res = testapp.get('/two', status=200) self.assertTrue(b'Two!' in res.body) class DummyContext(object): pass class DummyRequest: subpath = ('__init__.py',) traversed = None environ = {'REQUEST_METHOD':'GET', 'wsgi.version':(1,0)} def get_response(self, application): return application(None, None) def httpdate(ts): return ts.strftime("%a, %d %b %Y %H:%M:%S GMT") def read_(filename): with open(filename, 'rb') as fp: val = fp.read() return val def _assertBody(body, filename): if defaultlocale is None: # pragma: no cover # If system locale does not have an encoding then default to utf-8 filename = filename.encode('utf-8') # strip both \n and \r for windows body = body.replace(b'\r', b'') body = body.replace(b'\n', b'') data = read_(filename) data = data.replace(b'\r', b'') data = data.replace(b'\n', b'') assert(body == data) pyramid-1.4.5/pyramid/tests/test_decorator.py0000664000175000017500000000147711752470445020706 0ustar takakitakakiimport unittest class TestReify(unittest.TestCase): def _makeOne(self, wrapped): from pyramid.decorator import reify return reify(wrapped) def test___get__withinst(self): def wrapped(inst): return 'a' decorator = self._makeOne(wrapped) inst = Dummy() result = decorator.__get__(inst) self.assertEqual(result, 'a') self.assertEqual(inst.__dict__['wrapped'], 'a') def test___get__noinst(self): decorator = self._makeOne(None) result = decorator.__get__(None) self.assertEqual(result, decorator) def test___doc__copied(self): def wrapped(inst): """My doc""" decorator = self._makeOne(wrapped) self.assertEqual(decorator.__doc__, "My doc") class Dummy(object): pass pyramid-1.4.5/pyramid/tests/test_response.py0000664000175000017500000001077212203712502020541 0ustar takakitakakiimport io import os import unittest from pyramid import testing class TestResponse(unittest.TestCase): def _getTargetClass(self): from pyramid.response import Response return Response def test_implements_IResponse(self): from pyramid.interfaces import IResponse cls = self._getTargetClass() self.assertTrue(IResponse.implementedBy(cls)) def test_provides_IResponse(self): from pyramid.interfaces import IResponse inst = self._getTargetClass()() self.assertTrue(IResponse.providedBy(inst)) class TestFileResponse(unittest.TestCase): def _makeOne(self, file, **kw): from pyramid.response import FileResponse return FileResponse(file, **kw) def _getPath(self): here = os.path.dirname(__file__) return os.path.join(here, 'fixtures', 'minimal.txt') def test_with_content_type(self): path = self._getPath() r = self._makeOne(path, content_type='image/jpeg') self.assertEqual(r.content_type, 'image/jpeg') r.app_iter.close() def test_without_content_type(self): path = self._getPath() r = self._makeOne(path) self.assertEqual(r.content_type, 'text/plain') r.app_iter.close() class TestFileIter(unittest.TestCase): def _makeOne(self, file, block_size): from pyramid.response import FileIter return FileIter(file, block_size) def test___iter__(self): f = io.BytesIO(b'abc') inst = self._makeOne(f, 1) self.assertEqual(inst.__iter__(), inst) def test_iteration(self): data = b'abcdef' f = io.BytesIO(b'abcdef') inst = self._makeOne(f, 1) r = b'' for x in inst: self.assertEqual(len(x), 1) r+=x self.assertEqual(r, data) def test_close(self): f = io.BytesIO(b'abc') inst = self._makeOne(f, 1) inst.close() self.assertTrue(f.closed) class Test_patch_mimetypes(unittest.TestCase): def _callFUT(self, module): from pyramid.response import init_mimetypes return init_mimetypes(module) def test_has_init(self): class DummyMimetypes(object): def init(self): self.initted = True module = DummyMimetypes() result = self._callFUT(module) self.assertEqual(result, True) self.assertEqual(module.initted, True) def test_missing_init(self): class DummyMimetypes(object): pass module = DummyMimetypes() result = self._callFUT(module) self.assertEqual(result, False) class TestResponseAdapter(unittest.TestCase): def setUp(self): registry = Dummy() self.config = testing.setUp(registry=registry) def tearDown(self): self.config.end() def _makeOne(self, *types_or_ifaces): from pyramid.response import response_adapter return response_adapter(*types_or_ifaces) def test_register_single(self): from zope.interface import Interface class IFoo(Interface): pass dec = self._makeOne(IFoo) def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.adapters, [(foo, IFoo)]) def test_register_multi(self): from zope.interface import Interface class IFoo(Interface): pass class IBar(Interface): pass dec = self._makeOne(IFoo, IBar) def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.adapters, [(foo, IFoo), (foo, IBar)]) def test___call__(self): from zope.interface import Interface class IFoo(Interface): pass dec = self._makeOne(IFoo) dummy_venusian = DummyVenusian() dec.venusian = dummy_venusian def foo(): pass dec(foo) self.assertEqual(dummy_venusian.attached, [(foo, dec.register, 'pyramid')]) class Dummy(object): pass class DummyConfigurator(object): def __init__(self): self.adapters = [] def add_response_adapter(self, wrapped, type_or_iface): self.adapters.append((wrapped, type_or_iface)) class DummyVenusian(object): def __init__(self): self.attached = [] def attach(self, wrapped, fn, category=None): self.attached.append((wrapped, fn, category)) pyramid-1.4.5/pyramid/tests/test_traversal.py0000664000175000017500000014330312210154301020676 0ustar takakitakakiimport unittest import warnings from pyramid.testing import cleanUp from pyramid.compat import ( text_, native_, text_type, url_quote, PY3, ) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always') from pyramid.interfaces import IContextURL assert(len(w) == 1) class TraversalPathTests(unittest.TestCase): def _callFUT(self, path): from pyramid.traversal import traversal_path return traversal_path(path) def test_utf8(self): la = b'La Pe\xc3\xb1a' encoded = url_quote(la) decoded = text_(la, 'utf-8') path = '/'.join([encoded, encoded]) result = self._callFUT(path) self.assertEqual(result, (decoded, decoded)) def test_utf16(self): from pyramid.exceptions import URLDecodeError la = text_(b'La Pe\xc3\xb1a', 'utf-8').encode('utf-16') encoded = url_quote(la) path = '/'.join([encoded, encoded]) self.assertRaises(URLDecodeError, self._callFUT, path) def test_unicode_highorder_chars(self): path = text_('/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF') self.assertEqual(self._callFUT(path), (text_('\u6d41\u884c\u8d8b\u52bf', 'unicode_escape'),)) def test_element_urllquoted(self): self.assertEqual(self._callFUT('/foo/space%20thing/bar'), (text_('foo'), text_('space thing'), text_('bar'))) def test_unicode_undecodeable_to_ascii(self): path = text_(b'/La Pe\xc3\xb1a', 'utf-8') self.assertRaises(UnicodeEncodeError, self._callFUT, path) class TraversalPathInfoTests(unittest.TestCase): def _callFUT(self, path): from pyramid.traversal import traversal_path_info return traversal_path_info(path) def test_path_startswith_endswith(self): self.assertEqual(self._callFUT('/foo/'), (text_('foo'),)) def test_empty_elements(self): self.assertEqual(self._callFUT('foo///'), (text_('foo'),)) def test_onedot(self): self.assertEqual(self._callFUT('foo/./bar'), (text_('foo'), text_('bar'))) def test_twodots(self): self.assertEqual(self._callFUT('foo/../bar'), (text_('bar'),)) def test_twodots_at_start(self): self.assertEqual(self._callFUT('../../bar'), (text_('bar'),)) def test_segments_are_unicode(self): result = self._callFUT('/foo/bar') self.assertEqual(type(result[0]), text_type) self.assertEqual(type(result[1]), text_type) def test_same_value_returned_if_cached(self): result1 = self._callFUT('/foo/bar') result2 = self._callFUT('/foo/bar') self.assertEqual(result1, (text_('foo'), text_('bar'))) self.assertEqual(result2, (text_('foo'), text_('bar'))) def test_unicode_simple(self): path = text_('/abc') self.assertEqual(self._callFUT(path), (text_('abc'),)) def test_highorder(self): la = b'La Pe\xc3\xb1a' latin1 = native_(la) result = self._callFUT(latin1) self.assertEqual(result, (text_(la, 'utf-8'),)) def test_highorder_undecodeable(self): from pyramid.exceptions import URLDecodeError la = text_(b'La Pe\xc3\xb1a', 'utf-8') notlatin1 = native_(la) self.assertRaises(URLDecodeError, self._callFUT, notlatin1) class ResourceTreeTraverserTests(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _getTargetClass(self): from pyramid.traversal import ResourceTreeTraverser return ResourceTreeTraverser def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def _getEnviron(self, **kw): environ = {} environ.update(kw) return environ def test_class_conforms_to_ITraverser(self): from zope.interface.verify import verifyClass from pyramid.interfaces import ITraverser verifyClass(ITraverser, self._getTargetClass()) def test_instance_conforms_to_ITraverser(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ITraverser context = DummyContext() verifyObject(ITraverser, self._makeOne(context)) def test_call_with_empty_pathinfo(self): policy = self._makeOne(None) environ = self._getEnviron() request = DummyRequest(environ, path_info='') result = policy(request) self.assertEqual(result['context'], None) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], policy.root) self.assertEqual(result['virtual_root'], policy.root) self.assertEqual(result['virtual_root_path'], ()) def test_call_with_pathinfo_KeyError(self): policy = self._makeOne(None) environ = self._getEnviron() request = DummyRequest(environ, toraise=KeyError) result = policy(request) self.assertEqual(result['context'], None) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], policy.root) self.assertEqual(result['virtual_root'], policy.root) self.assertEqual(result['virtual_root_path'], ()) def test_call_with_pathinfo_highorder(self): path = text_(b'/Qu\xc3\xa9bec', 'utf-8') foo = DummyContext(None, path) root = DummyContext(foo, 'root') policy = self._makeOne(root) environ = self._getEnviron() request = DummyRequest(environ, path_info=path) result = policy(request) self.assertEqual(result['context'], foo) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (path[1:],)) self.assertEqual(result['root'], policy.root) self.assertEqual(result['virtual_root'], policy.root) self.assertEqual(result['virtual_root_path'], ()) def test_call_pathel_with_no_getitem(self): policy = self._makeOne(None) environ = self._getEnviron() request = DummyRequest(environ, path_info=text_('/foo/bar')) result = policy(request) self.assertEqual(result['context'], None) self.assertEqual(result['view_name'], 'foo') self.assertEqual(result['subpath'], ('bar',)) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], policy.root) self.assertEqual(result['virtual_root'], policy.root) self.assertEqual(result['virtual_root_path'], ()) def test_call_withconn_getitem_emptypath_nosubpath(self): root = DummyContext() policy = self._makeOne(root) environ = self._getEnviron() request = DummyRequest(environ, path_info=text_('')) result = policy(request) self.assertEqual(result['context'], root) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], root) self.assertEqual(result['virtual_root_path'], ()) def test_call_withconn_getitem_withpath_nosubpath(self): foo = DummyContext() root = DummyContext(foo) policy = self._makeOne(root) environ = self._getEnviron() request = DummyRequest(environ, path_info=text_('/foo/bar')) result = policy(request) self.assertEqual(result['context'], foo) self.assertEqual(result['view_name'], 'bar') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('foo'),)) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], root) self.assertEqual(result['virtual_root_path'], ()) def test_call_withconn_getitem_withpath_withsubpath(self): foo = DummyContext() root = DummyContext(foo) policy = self._makeOne(root) environ = self._getEnviron() request = DummyRequest(environ, path_info=text_('/foo/bar/baz/buz')) result = policy(request) self.assertEqual(result['context'], foo) self.assertEqual(result['view_name'], 'bar') self.assertEqual(result['subpath'], ('baz', 'buz')) self.assertEqual(result['traversed'], (text_('foo'),)) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], root) self.assertEqual(result['virtual_root_path'], ()) def test_call_with_explicit_viewname(self): foo = DummyContext() root = DummyContext(foo) policy = self._makeOne(root) environ = self._getEnviron() request = DummyRequest(environ, path_info=text_('/@@foo')) result = policy(request) self.assertEqual(result['context'], root) self.assertEqual(result['view_name'], 'foo') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], root) self.assertEqual(result['virtual_root_path'], ()) def test_call_with_vh_root(self): environ = self._getEnviron(HTTP_X_VHM_ROOT='/foo/bar') baz = DummyContext(None, 'baz') bar = DummyContext(baz, 'bar') foo = DummyContext(bar, 'foo') root = DummyContext(foo, 'root') policy = self._makeOne(root) request = DummyRequest(environ, path_info=text_('/baz')) result = policy(request) self.assertEqual(result['context'], baz) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('foo'), text_('bar'), text_('baz'))) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], bar) self.assertEqual(result['virtual_root_path'], (text_('foo'), text_('bar'))) def test_call_with_vh_root2(self): environ = self._getEnviron(HTTP_X_VHM_ROOT='/foo') baz = DummyContext(None, 'baz') bar = DummyContext(baz, 'bar') foo = DummyContext(bar, 'foo') root = DummyContext(foo, 'root') policy = self._makeOne(root) request = DummyRequest(environ, path_info=text_('/bar/baz')) result = policy(request) self.assertEqual(result['context'], baz) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('foo'), text_('bar'), text_('baz'))) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], foo) self.assertEqual(result['virtual_root_path'], (text_('foo'),)) def test_call_with_vh_root3(self): environ = self._getEnviron(HTTP_X_VHM_ROOT='/') baz = DummyContext() bar = DummyContext(baz) foo = DummyContext(bar) root = DummyContext(foo) policy = self._makeOne(root) request = DummyRequest(environ, path_info=text_('/foo/bar/baz')) result = policy(request) self.assertEqual(result['context'], baz) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('foo'), text_('bar'), text_('baz'))) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], root) self.assertEqual(result['virtual_root_path'], ()) def test_call_with_vh_root4(self): environ = self._getEnviron(HTTP_X_VHM_ROOT='/foo/bar/baz') baz = DummyContext(None, 'baz') bar = DummyContext(baz, 'bar') foo = DummyContext(bar, 'foo') root = DummyContext(foo, 'root') policy = self._makeOne(root) request = DummyRequest(environ, path_info=text_('/')) result = policy(request) self.assertEqual(result['context'], baz) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], (text_('foo'), text_('bar'), text_('baz'))) self.assertEqual(result['root'], root) self.assertEqual(result['virtual_root'], baz) self.assertEqual(result['virtual_root_path'], (text_('foo'), text_('bar'), text_('baz'))) def test_call_with_vh_root_path_root(self): policy = self._makeOne(None) environ = self._getEnviron(HTTP_X_VHM_ROOT='/') request = DummyRequest(environ, path_info=text_('/')) result = policy(request) self.assertEqual(result['context'], None) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], policy.root) self.assertEqual(result['virtual_root'], policy.root) self.assertEqual(result['virtual_root_path'], ()) def test_call_with_vh_root_highorder(self): path = text_(b'Qu\xc3\xa9bec', 'utf-8') bar = DummyContext(None, 'bar') foo = DummyContext(bar, path) root = DummyContext(foo, 'root') policy = self._makeOne(root) if PY3: # pragma: no cover vhm_root = b'/Qu\xc3\xa9bec'.decode('latin-1') else: vhm_root = b'/Qu\xc3\xa9bec' environ = self._getEnviron(HTTP_X_VHM_ROOT=vhm_root) request = DummyRequest(environ, path_info=text_('/bar')) result = policy(request) self.assertEqual(result['context'], bar) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual( result['traversed'], (path, text_('bar')) ) self.assertEqual(result['root'], policy.root) self.assertEqual(result['virtual_root'], foo) self.assertEqual( result['virtual_root_path'], (path,) ) def test_path_info_raises_unicodedecodeerror(self): from pyramid.exceptions import URLDecodeError foo = DummyContext() root = DummyContext(foo) policy = self._makeOne(root) environ = self._getEnviron() toraise = UnicodeDecodeError('ascii', b'a', 2, 3, '5') request = DummyRequest(environ, toraise=toraise) request.matchdict = None self.assertRaises(URLDecodeError, policy, request) def test_withroute_nothingfancy(self): resource = DummyContext() traverser = self._makeOne(resource) request = DummyRequest({}) request.matchdict = {} result = traverser(request) self.assertEqual(result['context'], resource) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], resource) self.assertEqual(result['virtual_root_path'], ()) def test_withroute_with_subpath_string(self): resource = DummyContext() traverser = self._makeOne(resource) matchdict = {'subpath':'/a/b/c'} request = DummyRequest({}) request.matchdict = matchdict result = traverser(request) self.assertEqual(result['context'], resource) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ('a', 'b','c')) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], resource) self.assertEqual(result['virtual_root_path'], ()) def test_withroute_with_subpath_tuple(self): resource = DummyContext() traverser = self._makeOne(resource) matchdict = {'subpath':('a', 'b', 'c')} request = DummyRequest({}) request.matchdict = matchdict result = traverser(request) self.assertEqual(result['context'], resource) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ('a', 'b','c')) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], resource) self.assertEqual(result['virtual_root_path'], ()) def test_withroute_and_traverse_string(self): resource = DummyContext() traverser = self._makeOne(resource) matchdict = {'traverse':text_('foo/bar')} request = DummyRequest({}) request.matchdict = matchdict result = traverser(request) self.assertEqual(result['context'], resource) self.assertEqual(result['view_name'], 'foo') self.assertEqual(result['subpath'], ('bar',)) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], resource) self.assertEqual(result['virtual_root_path'], ()) def test_withroute_and_traverse_tuple(self): resource = DummyContext() traverser = self._makeOne(resource) matchdict = {'traverse':('foo', 'bar')} request = DummyRequest({}) request.matchdict = matchdict result = traverser(request) self.assertEqual(result['context'], resource) self.assertEqual(result['view_name'], 'foo') self.assertEqual(result['subpath'], ('bar',)) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], resource) self.assertEqual(result['virtual_root_path'], ()) def test_withroute_and_traverse_empty(self): resource = DummyContext() traverser = self._makeOne(resource) matchdict = {'traverse':''} request = DummyRequest({}) request.matchdict = matchdict result = traverser(request) self.assertEqual(result['context'], resource) self.assertEqual(result['view_name'], '') self.assertEqual(result['subpath'], ()) self.assertEqual(result['traversed'], ()) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], resource) self.assertEqual(result['virtual_root_path'], ()) def test_withroute_and_traverse_and_vroot(self): abc = DummyContext() resource = DummyContext(next=abc) environ = self._getEnviron(HTTP_X_VHM_ROOT='/abc') request = DummyRequest(environ) traverser = self._makeOne(resource) matchdict = {'traverse':text_('/foo/bar')} request.matchdict = matchdict result = traverser(request) self.assertEqual(result['context'], abc) self.assertEqual(result['view_name'], 'foo') self.assertEqual(result['subpath'], ('bar',)) self.assertEqual(result['traversed'], ('abc', 'foo')) self.assertEqual(result['root'], resource) self.assertEqual(result['virtual_root'], abc) self.assertEqual(result['virtual_root_path'], ('abc',)) class FindInterfaceTests(unittest.TestCase): def _callFUT(self, context, iface): from pyramid.traversal import find_interface return find_interface(context, iface) def test_it_interface(self): baz = DummyContext() bar = DummyContext(baz) foo = DummyContext(bar) root = DummyContext(foo) root.__parent__ = None root.__name__ = 'root' foo.__parent__ = root foo.__name__ = 'foo' bar.__parent__ = foo bar.__name__ = 'bar' baz.__parent__ = bar baz.__name__ = 'baz' from zope.interface import directlyProvides from zope.interface import Interface class IFoo(Interface): pass directlyProvides(root, IFoo) result = self._callFUT(baz, IFoo) self.assertEqual(result.__name__, 'root') def test_it_class(self): class DummyRoot(object): def __init__(self, child): self.child = child baz = DummyContext() bar = DummyContext(baz) foo = DummyContext(bar) root = DummyRoot(foo) root.__parent__ = None root.__name__ = 'root' foo.__parent__ = root foo.__name__ = 'foo' bar.__parent__ = foo bar.__name__ = 'bar' baz.__parent__ = bar baz.__name__ = 'baz' result = self._callFUT(baz, DummyRoot) self.assertEqual(result.__name__, 'root') class FindRootTests(unittest.TestCase): def _callFUT(self, context): from pyramid.traversal import find_root return find_root(context) def test_it(self): dummy = DummyContext() baz = DummyContext() baz.__parent__ = dummy baz.__name__ = 'baz' dummy.__parent__ = None dummy.__name__ = None result = self._callFUT(baz) self.assertEqual(result, dummy) class FindResourceTests(unittest.TestCase): def _callFUT(self, context, name): from pyramid.traversal import find_resource return find_resource(context, name) def _registerTraverser(self, traverser): from pyramid.threadlocal import get_current_registry reg = get_current_registry() from pyramid.interfaces import ITraverser from zope.interface import Interface reg.registerAdapter(traverser, (Interface,), ITraverser) def test_list(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, ['']) self.assertEqual(result, resource) self.assertEqual(resource.request.environ['PATH_INFO'], '/') def test_generator(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) def foo(): yield '' result = self._callFUT(resource, foo()) self.assertEqual(result, resource) self.assertEqual(resource.request.environ['PATH_INFO'], '/') def test_self_string_found(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, '') self.assertEqual(result, resource) self.assertEqual(resource.request.environ['PATH_INFO'], '') def test_self_tuple_found(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, ()) self.assertEqual(result, resource) self.assertEqual(resource.request.environ['PATH_INFO'], '') def test_relative_string_found(self): resource = DummyContext() baz = DummyContext() traverser = make_traverser({'context':baz, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, 'baz') self.assertEqual(result, baz) self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') def test_relative_tuple_found(self): resource = DummyContext() baz = DummyContext() traverser = make_traverser({'context':baz, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, ('baz',)) self.assertEqual(result, baz) self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') def test_relative_string_notfound(self): resource = DummyContext() baz = DummyContext() traverser = make_traverser({'context':baz, 'view_name':'bar'}) self._registerTraverser(traverser) self.assertRaises(KeyError, self._callFUT, resource, 'baz') self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') def test_relative_tuple_notfound(self): resource = DummyContext() baz = DummyContext() traverser = make_traverser({'context':baz, 'view_name':'bar'}) self._registerTraverser(traverser) self.assertRaises(KeyError, self._callFUT, resource, ('baz',)) self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') def test_absolute_string_found(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, '/') self.assertEqual(result, root) self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') def test_absolute_tuple_found(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':''}) self._registerTraverser(traverser) result = self._callFUT(resource, ('',)) self.assertEqual(result, root) self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') def test_absolute_string_notfound(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':'fuz'}) self._registerTraverser(traverser) self.assertRaises(KeyError, self._callFUT, resource, '/') self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') def test_absolute_tuple_notfound(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':'fuz'}) self._registerTraverser(traverser) self.assertRaises(KeyError, self._callFUT, resource, ('',)) self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') def test_absolute_unicode_found(self): # test for bug wiggy found in wild, traceback stack: # root = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF' # wiggy's code: section=find_resource(page, root) # find_resource L76: D = traverse(resource, path) # traverse L291: return traverser(request) # __call__ line 568: vpath_tuple = traversal_path(vpath) # lru_cached line 91: f(*arg) # traversal_path line 443: path.encode('ascii') # UnicodeEncodeError: 'ascii' codec can't encode characters in # position 1-12: ordinal not in range(128) # # solution: encode string to ascii in pyramid.traversal.traverse # before passing it along to webob as path_info from pyramid.traversal import ResourceTreeTraverser unprintable = DummyContext() root = DummyContext(unprintable) unprintable.__parent__ = root unprintable.__name__ = text_( b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8') root.__parent__ = None root.__name__ = None traverser = ResourceTreeTraverser self._registerTraverser(traverser) result = self._callFUT( root, text_(b'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF') ) self.assertEqual(result, unprintable) class ResourcePathTests(unittest.TestCase): def _callFUT(self, resource, *elements): from pyramid.traversal import resource_path return resource_path(resource, *elements) def test_it(self): baz = DummyContext() bar = DummyContext(baz) foo = DummyContext(bar) root = DummyContext(foo) root.__parent__ = None root.__name__ = None foo.__parent__ = root foo.__name__ = 'foo ' bar.__parent__ = foo bar.__name__ = 'bar' baz.__parent__ = bar baz.__name__ = 'baz' result = self._callFUT(baz, 'this/theotherthing', 'that') self.assertEqual(result, '/foo%20/bar/baz/this%2Ftheotherthing/that') def test_root_default(self): root = DummyContext() root.__parent__ = None root.__name__ = None result = self._callFUT(root) self.assertEqual(result, '/') def test_root_default_emptystring(self): root = DummyContext() root.__parent__ = None root.__name__ = '' result = self._callFUT(root) self.assertEqual(result, '/') def test_root_object_nonnull_name_direct(self): root = DummyContext() root.__parent__ = None root.__name__ = 'flubadub' result = self._callFUT(root) self.assertEqual(result, 'flubadub') # insane case def test_root_object_nonnull_name_indirect(self): root = DummyContext() root.__parent__ = None root.__name__ = 'flubadub' other = DummyContext() other.__parent__ = root other.__name__ = 'barker' result = self._callFUT(other) self.assertEqual(result, 'flubadub/barker') # insane case def test_nonroot_default(self): root = DummyContext() root.__parent__ = None root.__name__ = None other = DummyContext() other.__parent__ = root other.__name__ = 'other' result = self._callFUT(other) self.assertEqual(result, '/other') def test_path_with_None_itermediate_names(self): root = DummyContext() root.__parent__ = None root.__name__ = None other = DummyContext() other.__parent__ = root other.__name__ = None other2 = DummyContext() other2.__parent__ = other other2.__name__ = 'other2' result = self._callFUT(other2) self.assertEqual(result, '//other2') class ResourcePathTupleTests(unittest.TestCase): def _callFUT(self, resource, *elements): from pyramid.traversal import resource_path_tuple return resource_path_tuple(resource, *elements) def test_it(self): baz = DummyContext() bar = DummyContext(baz) foo = DummyContext(bar) root = DummyContext(foo) root.__parent__ = None root.__name__ = None foo.__parent__ = root foo.__name__ = 'foo ' bar.__parent__ = foo bar.__name__ = 'bar' baz.__parent__ = bar baz.__name__ = 'baz' result = self._callFUT(baz, 'this/theotherthing', 'that') self.assertEqual(result, ('','foo ', 'bar', 'baz', 'this/theotherthing', 'that')) def test_root_default(self): root = DummyContext() root.__parent__ = None root.__name__ = None result = self._callFUT(root) self.assertEqual(result, ('',)) def test_root_default_emptystring_name(self): root = DummyContext() root.__parent__ = None root.__name__ = '' other = DummyContext() other.__parent__ = root other.__name__ = 'other' result = self._callFUT(other) self.assertEqual(result, ('', 'other',)) def test_nonroot_default(self): root = DummyContext() root.__parent__ = None root.__name__ = None other = DummyContext() other.__parent__ = root other.__name__ = 'other' result = self._callFUT(other) self.assertEqual(result, ('', 'other')) def test_path_with_None_itermediate_names(self): root = DummyContext() root.__parent__ = None root.__name__ = None other = DummyContext() other.__parent__ = root other.__name__ = None other2 = DummyContext() other2.__parent__ = other other2.__name__ = 'other2' result = self._callFUT(other2) self.assertEqual(result, ('', '', 'other2')) class QuotePathSegmentTests(unittest.TestCase): def _callFUT(self, s): from pyramid.traversal import quote_path_segment return quote_path_segment(s) def test_unicode(self): la = text_(b'/La Pe\xc3\xb1a', 'utf-8') result = self._callFUT(la) self.assertEqual(result, '%2FLa%20Pe%C3%B1a') def test_string(self): s = '/ hello!' result = self._callFUT(s) self.assertEqual(result, '%2F%20hello%21') def test_int(self): s = 12345 result = self._callFUT(s) self.assertEqual(result, '12345') def test_long(self): from pyramid.compat import long import sys s = long(sys.maxsize + 1) result = self._callFUT(s) expected = str(s) self.assertEqual(result, expected) def test_other(self): class Foo(object): def __str__(self): return 'abc' s = Foo() result = self._callFUT(s) self.assertEqual(result, 'abc') class ResourceURLTests(unittest.TestCase): def _makeOne(self, context, url): return self._getTargetClass()(context, url) def _getTargetClass(self): from pyramid.traversal import ResourceURL return ResourceURL def _registerTraverser(self, traverser): from pyramid.threadlocal import get_current_registry reg = get_current_registry() from pyramid.interfaces import ITraverser from zope.interface import Interface reg.registerAdapter(traverser, (Interface,), ITraverser) def test_class_conforms_to_IContextURL(self): # bw compat from zope.interface.verify import verifyClass verifyClass(IContextURL, self._getTargetClass()) def test_instance_conforms_to_IContextURL(self): from zope.interface.verify import verifyObject context = DummyContext() request = DummyRequest() verifyObject(IContextURL, self._makeOne(context, request)) def test_instance_conforms_to_IResourceURL(self): from pyramid.interfaces import IResourceURL from zope.interface.verify import verifyObject context = DummyContext() request = DummyRequest() verifyObject(IResourceURL, self._makeOne(context, request)) def test_call_withlineage(self): baz = DummyContext() bar = DummyContext(baz) foo = DummyContext(bar) root = DummyContext(foo) root.__parent__ = None root.__name__ = None foo.__parent__ = root foo.__name__ = 'foo ' bar.__parent__ = foo bar.__name__ = 'bar' baz.__parent__ = bar baz.__name__ = 'baz' request = DummyRequest() context_url = self._makeOne(baz, request) result = context_url() self.assertEqual(result, 'http://example.com:5432/foo%20/bar/baz/') def test_call_nolineage(self): context = DummyContext() context.__name__ = '' context.__parent__ = None request = DummyRequest() context_url = self._makeOne(context, request) result = context_url() self.assertEqual(result, 'http://example.com:5432/') def test_call_unicode_mixed_with_bytes_in_resource_names(self): root = DummyContext() root.__parent__ = None root.__name__ = None one = DummyContext() one.__parent__ = root one.__name__ = text_(b'La Pe\xc3\xb1a', 'utf-8') two = DummyContext() two.__parent__ = one two.__name__ = b'La Pe\xc3\xb1a' request = DummyRequest() context_url = self._makeOne(two, request) result = context_url() self.assertEqual( result, 'http://example.com:5432/La%20Pe%C3%B1a/La%20Pe%C3%B1a/') def test_call_with_virtual_root_path(self): from pyramid.interfaces import VH_ROOT_KEY root = DummyContext() root.__parent__ = None root.__name__ = None one = DummyContext() one.__parent__ = root one.__name__ = 'one' two = DummyContext() two.__parent__ = one two.__name__ = 'two' request = DummyRequest({VH_ROOT_KEY:'/one'}) context_url = self._makeOne(two, request) result = context_url() self.assertEqual(result, 'http://example.com:5432/two/') request = DummyRequest({VH_ROOT_KEY:'/one/two'}) context_url = self._makeOne(two, request) result = context_url() self.assertEqual(result, 'http://example.com:5432/') def test_call_with_virtual_root_path_physical_not_startwith_vroot(self): from pyramid.interfaces import VH_ROOT_KEY root = DummyContext() root.__parent__ = None root.__name__ = None one = DummyContext() one.__parent__ = root one.__name__ = 'one' two = DummyContext() two.__parent__ = one two.__name__ = 'two' request = DummyRequest({VH_ROOT_KEY:'/wrong'}) context_url = self._makeOne(two, request) result = context_url() self.assertEqual(result, 'http://example.com:5432/one/two/') def test_call_empty_names_not_ignored(self): bar = DummyContext() empty = DummyContext(bar) root = DummyContext(empty) root.__parent__ = None root.__name__ = None empty.__parent__ = root empty.__name__ = '' bar.__parent__ = empty bar.__name__ = 'bar' request = DummyRequest() context_url = self._makeOne(bar, request) result = context_url() self.assertEqual(result, 'http://example.com:5432//bar/') def test_call_local_url_returns_None(self): resource = DummyContext() def resource_url(request, info): self.assertEqual(info['virtual_path'], '/') self.assertEqual(info['physical_path'], '/') return None resource.__resource_url__ = resource_url request = DummyRequest() context_url = self._makeOne(resource, request) result = context_url() self.assertEqual(result, 'http://example.com:5432/') def test_call_local_url_returns_url(self): resource = DummyContext() def resource_url(request, info): self.assertEqual(info['virtual_path'], '/') self.assertEqual(info['physical_path'], '/') return 'abc' resource.__resource_url__ = resource_url request = DummyRequest() context_url = self._makeOne(resource, request) result = context_url() self.assertEqual(result, 'abc') def test_virtual_root_no_virtual_root_path(self): root = DummyContext() root.__name__ = None root.__parent__ = None one = DummyContext() one.__name__ = 'one' one.__parent__ = root request = DummyRequest() context_url = self._makeOne(one, request) self.assertEqual(context_url.virtual_root(), root) def test_virtual_root_no_virtual_root_path_with_root_on_request(self): context = DummyContext() context.__parent__ = None request = DummyRequest() request.root = DummyContext() context_url = self._makeOne(context, request) self.assertEqual(context_url.virtual_root(), request.root) def test_virtual_root_with_virtual_root_path(self): from pyramid.interfaces import VH_ROOT_KEY context = DummyContext() context.__parent__ = None traversed_to = DummyContext() environ = {VH_ROOT_KEY:'/one'} request = DummyRequest(environ) traverser = make_traverser({'context':traversed_to, 'view_name':''}) self._registerTraverser(traverser) context_url = self._makeOne(context, request) self.assertEqual(context_url.virtual_root(), traversed_to) self.assertEqual(context.request.environ['PATH_INFO'], '/one') def test_IResourceURL_attributes_with_vroot(self): from pyramid.interfaces import VH_ROOT_KEY root = DummyContext() root.__parent__ = None root.__name__ = None one = DummyContext() one.__parent__ = root one.__name__ = 'one' two = DummyContext() two.__parent__ = one two.__name__ = 'two' environ = {VH_ROOT_KEY:'/one'} request = DummyRequest(environ) context_url = self._makeOne(two, request) self.assertEqual(context_url.physical_path, '/one/two/') self.assertEqual(context_url.virtual_path, '/two/') def test_IResourceURL_attributes_no_vroot(self): root = DummyContext() root.__parent__ = None root.__name__ = None one = DummyContext() one.__parent__ = root one.__name__ = 'one' two = DummyContext() two.__parent__ = one two.__name__ = 'two' environ = {} request = DummyRequest(environ) context_url = self._makeOne(two, request) self.assertEqual(context_url.physical_path, '/one/two/') self.assertEqual(context_url.virtual_path, '/one/two/') class TestVirtualRoot(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, resource, request): from pyramid.traversal import virtual_root return virtual_root(resource, request) def test_registered(self): from zope.interface import Interface request = _makeRequest() request.registry.registerAdapter(DummyContextURL, (Interface,Interface), IContextURL) context = DummyContext() result = self._callFUT(context, request) self.assertEqual(result, '123') def test_default(self): context = DummyContext() request = _makeRequest() request.environ['PATH_INFO'] = '/' result = self._callFUT(context, request) self.assertEqual(result, context) def test_default_no_registry_on_request(self): context = DummyContext() request = _makeRequest() del request.registry request.environ['PATH_INFO'] = '/' result = self._callFUT(context, request) self.assertEqual(result, context) class TraverseTests(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, context, name): from pyramid.traversal import traverse return traverse(context, name) def _registerTraverser(self, traverser): from pyramid.threadlocal import get_current_registry reg = get_current_registry() from pyramid.interfaces import ITraverser from zope.interface import Interface reg.registerAdapter(traverser, (Interface,), ITraverser) def test_request_has_registry(self): from pyramid.threadlocal import get_current_registry resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, ['']) self.assertEqual(resource.request.registry, get_current_registry()) def test_list(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, ['']) self.assertEqual(resource.request.environ['PATH_INFO'], '/') def test_generator(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) def foo(): yield '' self._callFUT(resource, foo()) self.assertEqual(resource.request.environ['PATH_INFO'], '/') def test_self_string_found(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, '') self.assertEqual(resource.request.environ['PATH_INFO'], '') def test_self_unicode_found(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, text_('')) self.assertEqual(resource.request.environ['PATH_INFO'], '') def test_self_tuple_found(self): resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, ()) self.assertEqual(resource.request.environ['PATH_INFO'], '') def test_relative_string_found(self): resource = DummyContext() baz = DummyContext() traverser = make_traverser({'context':baz, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, 'baz') self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') def test_relative_tuple_found(self): resource = DummyContext() baz = DummyContext() traverser = make_traverser({'context':baz, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, ('baz',)) self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') def test_absolute_string_found(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, '/') self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') def test_absolute_tuple_found(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, ('',)) self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') def test_empty_sequence(self): root = DummyContext() resource = DummyContext() resource.__parent__ = root resource.__name__ = 'baz' traverser = make_traverser({'context':root, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, []) self.assertEqual(resource.wascontext, True) self.assertEqual(resource.request.environ['PATH_INFO'], '') def test_default_traverser(self): resource = DummyContext() result = self._callFUT(resource, '') self.assertEqual(result['view_name'], '') self.assertEqual(result['context'], resource) def test_requestfactory_overridden(self): from pyramid.interfaces import IRequestFactory from pyramid.request import Request from pyramid.threadlocal import get_current_registry reg = get_current_registry() class MyRequest(Request): pass reg.registerUtility(MyRequest, IRequestFactory) resource = DummyContext() traverser = make_traverser({'context':resource, 'view_name':''}) self._registerTraverser(traverser) self._callFUT(resource, ['']) self.assertEqual(resource.request.__class__, MyRequest) class TestDefaultRootFactory(unittest.TestCase): def _getTargetClass(self): from pyramid.traversal import DefaultRootFactory return DefaultRootFactory def _makeOne(self, environ): return self._getTargetClass()(environ) def test_no_matchdict(self): class DummyRequest: matchdict = None root = self._makeOne(DummyRequest()) self.assertEqual(root.__parent__, None) self.assertEqual(root.__name__, None) def test_matchdict(self): class DummyRequest: pass request = DummyRequest() request.matchdict = {'a':1, 'b':2} root = self._makeOne(request) self.assertEqual(root.a, 1) self.assertEqual(root.b, 2) class Test__join_path_tuple(unittest.TestCase): def _callFUT(self, tup): from pyramid.traversal import _join_path_tuple return _join_path_tuple(tup) def test_empty_tuple(self): # tests "or '/'" case result = self._callFUT(()) self.assertEqual(result, '/') def test_nonempty_tuple(self): result = self._callFUT(('x',)) self.assertEqual(result, 'x') def make_traverser(result): class DummyTraverser(object): def __init__(self, context): self.context = context context.wascontext = True def __call__(self, request): self.context.request = request return result return DummyTraverser class DummyContext(object): __parent__ = None def __init__(self, next=None, name=None): self.next = next self.__name__ = name def __getitem__(self, name): if self.next is None: raise KeyError(name) return self.next def __repr__(self): return ''%(self.__name__, id(self)) class DummyRequest: application_url = 'http://example.com:5432' # app_url never ends with slash matchdict = None matched_route = None def __init__(self, environ=None, path_info=text_('/'), toraise=None): if environ is None: environ = {} self.environ = environ self._set_path_info(path_info) self.toraise = toraise def _get_path_info(self): if self.toraise: raise self.toraise return self._path_info def _set_path_info(self, v): self._path_info = v path_info = property(_get_path_info, _set_path_info) class DummyContextURL: def __init__(self, context, request): pass def virtual_root(self): return '123' def _makeRequest(environ=None): from pyramid.registry import Registry request = DummyRequest() request.registry = Registry() return request pyramid-1.4.5/pyramid/tests/test_testing.py0000664000175000017500000006107312210154276020366 0ustar takakitakakiimport unittest class TestDummyRootFactory(unittest.TestCase): def _makeOne(self, environ): from pyramid.testing import DummyRootFactory return DummyRootFactory(environ) def test_it(self): environ = {'bfg.routes.matchdict':{'a':1}} factory = self._makeOne(environ) self.assertEqual(factory.a, 1) class TestDummySecurityPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.testing import DummySecurityPolicy return DummySecurityPolicy def _makeOne(self, userid=None, groupids=(), permissive=True): klass = self._getTargetClass() return klass(userid, groupids, permissive) def test_authenticated_userid(self): policy = self._makeOne('user') self.assertEqual(policy.authenticated_userid(None), 'user') def test_unauthenticated_userid(self): policy = self._makeOne('user') self.assertEqual(policy.unauthenticated_userid(None), 'user') def test_effective_principals_userid(self): policy = self._makeOne('user', ('group1',)) from pyramid.security import Everyone from pyramid.security import Authenticated self.assertEqual(policy.effective_principals(None), [Everyone, Authenticated, 'user', 'group1']) def test_effective_principals_nouserid(self): policy = self._makeOne() from pyramid.security import Everyone self.assertEqual(policy.effective_principals(None), [Everyone]) def test_permits(self): policy = self._makeOne() self.assertEqual(policy.permits(None, None, None), True) def test_principals_allowed_by_permission(self): policy = self._makeOne('user', ('group1',)) from pyramid.security import Everyone from pyramid.security import Authenticated result = policy.principals_allowed_by_permission(None, None) self.assertEqual(result, [Everyone, Authenticated, 'user', 'group1']) def test_forget(self): policy = self._makeOne() self.assertEqual(policy.forget(None), []) def test_remember(self): policy = self._makeOne() self.assertEqual(policy.remember(None, None), []) class TestDummyResource(unittest.TestCase): def _getTargetClass(self): from pyramid.testing import DummyResource return DummyResource def _makeOne(self, name=None, parent=None, **kw): klass = self._getTargetClass() return klass(name, parent, **kw) def test__setitem__and__getitem__and__delitem__and__contains__and_get(self): class Dummy: pass dummy = Dummy() resource = self._makeOne() resource['abc'] = dummy self.assertEqual(dummy.__name__, 'abc') self.assertEqual(dummy.__parent__, resource) self.assertEqual(resource['abc'], dummy) self.assertEqual(resource.get('abc'), dummy) self.assertRaises(KeyError, resource.__getitem__, 'none') self.assertTrue('abc' in resource) del resource['abc'] self.assertFalse('abc' in resource) self.assertEqual(resource.get('abc', 'foo'), 'foo') self.assertEqual(resource.get('abc'), None) def test_extra_params(self): resource = self._makeOne(foo=1) self.assertEqual(resource.foo, 1) def test_clone(self): resource = self._makeOne('name', 'parent', foo=1, bar=2) clone = resource.clone('name2', 'parent2', bar=1) self.assertEqual(clone.bar, 1) self.assertEqual(clone.__name__, 'name2') self.assertEqual(clone.__parent__, 'parent2') self.assertEqual(clone.foo, 1) def test_keys_items_values_len(self): class Dummy: pass resource = self._makeOne() resource['abc'] = Dummy() resource['def'] = Dummy() L = list self.assertEqual(L(resource.values()), L(resource.subs.values())) self.assertEqual(L(resource.items()), L(resource.subs.items())) self.assertEqual(L(resource.keys()), L(resource.subs.keys())) self.assertEqual(len(resource), 2) def test_nonzero(self): resource = self._makeOne() self.assertEqual(resource.__nonzero__(), True) def test_bool(self): resource = self._makeOne() self.assertEqual(resource.__bool__(), True) def test_ctor_with__provides__(self): resource = self._makeOne(__provides__=IDummy) self.assertTrue(IDummy.providedBy(resource)) class TestDummyRequest(unittest.TestCase): def _getTargetClass(self): from pyramid.testing import DummyRequest return DummyRequest def _makeOne(self, *arg, **kw): return self._getTargetClass()(*arg, **kw) def test_params(self): request = self._makeOne(params = {'say':'Hello'}, environ = {'PATH_INFO':'/foo'}, headers = {'X-Foo':'YUP'}, ) self.assertEqual(request.params['say'], 'Hello') self.assertEqual(request.GET['say'], 'Hello') self.assertEqual(request.POST['say'], 'Hello') self.assertEqual(request.headers['X-Foo'], 'YUP') self.assertEqual(request.environ['PATH_INFO'], '/foo') def test_defaults(self): from pyramid.threadlocal import get_current_registry from pyramid.testing import DummySession request = self._makeOne() self.assertEqual(request.method, 'GET') self.assertEqual(request.application_url, 'http://example.com') self.assertEqual(request.host_url, 'http://example.com') self.assertEqual(request.path_url, 'http://example.com') self.assertEqual(request.url, 'http://example.com') self.assertEqual(request.host, 'example.com:80') self.assertEqual(request.content_length, 0) self.assertEqual(request.environ.get('PATH_INFO'), None) self.assertEqual(request.headers.get('X-Foo'), None) self.assertEqual(request.params.get('foo'), None) self.assertEqual(request.GET.get('foo'), None) self.assertEqual(request.POST.get('foo'), None) self.assertEqual(request.cookies.get('type'), None) self.assertEqual(request.path, '/') self.assertEqual(request.path_info, '/') self.assertEqual(request.script_name, '') self.assertEqual(request.path_qs, '') self.assertEqual(request.view_name, '') self.assertEqual(request.subpath, ()) self.assertEqual(request.context, None) self.assertEqual(request.root, None) self.assertEqual(request.virtual_root, None) self.assertEqual(request.virtual_root_path, ()) self.assertEqual(request.registry, get_current_registry()) self.assertEqual(request.session.__class__, DummySession) def test_params_explicit(self): request = self._makeOne(params = {'foo':'bar'}) self.assertEqual(request.params['foo'], 'bar') self.assertEqual(request.GET['foo'], 'bar') self.assertEqual(request.POST['foo'], 'bar') def test_environ_explicit(self): request = self._makeOne(environ = {'PATH_INFO':'/foo'}) self.assertEqual(request.environ['PATH_INFO'], '/foo') def test_headers_explicit(self): request = self._makeOne(headers = {'X-Foo':'YUP'}) self.assertEqual(request.headers['X-Foo'], 'YUP') def test_path_explicit(self): request = self._makeOne(path = '/abc') self.assertEqual(request.path, '/abc') def test_cookies_explicit(self): request = self._makeOne(cookies = {'type': 'gingersnap'}) self.assertEqual(request.cookies['type'], 'gingersnap') def test_post_explicit(self): POST = {'foo': 'bar', 'baz': 'qux'} request = self._makeOne(post=POST) self.assertEqual(request.method, 'POST') self.assertEqual(request.POST, POST) # N.B.: Unlike a normal request, passing 'post' should *not* put # explict POST data into params: doing so masks a possible # XSS bug in the app. Tests for apps which don't care about # the distinction should just use 'params'. self.assertEqual(request.params, {}) def test_post_empty_shadows_params(self): request = self._makeOne(params={'foo': 'bar'}, post={}) self.assertEqual(request.method, 'POST') self.assertEqual(request.params.get('foo'), 'bar') self.assertEqual(request.POST.get('foo'), None) def test_kwargs(self): request = self._makeOne(water = 1) self.assertEqual(request.water, 1) def test_add_response_callback(self): request = self._makeOne() request.add_response_callback(1) self.assertEqual(request.response_callbacks, [1]) def test_registry_is_config_registry_when_setup_is_called_after_ctor(self): # see https://github.com/Pylons/pyramid/issues/165 from pyramid.registry import Registry from pyramid.config import Configurator request = self._makeOne() try: registry = Registry('this_test') config = Configurator(registry=registry) config.begin() self.assertTrue(request.registry is registry) finally: config.end() def test_set_registry(self): request = self._makeOne() request.registry = 'abc' self.assertEqual(request.registry, 'abc') def test_del_registry(self): # see https://github.com/Pylons/pyramid/issues/165 from pyramid.registry import Registry from pyramid.config import Configurator request = self._makeOne() request.registry = 'abc' self.assertEqual(request.registry, 'abc') del request.registry try: registry = Registry('this_test') config = Configurator(registry=registry) config.begin() self.assertTrue(request.registry is registry) finally: config.end() def test_response_with_responsefactory(self): from pyramid.registry import Registry from pyramid.interfaces import IResponseFactory registry = Registry('this_test') class ResponseFactory(object): pass registry.registerUtility(ResponseFactory, IResponseFactory) request = self._makeOne() request.registry = registry resp = request.response self.assertEqual(resp.__class__, ResponseFactory) self.assertTrue(request.response is resp) # reified def test_response_without_responsefactory(self): from pyramid.registry import Registry from pyramid.response import Response registry = Registry('this_test') request = self._makeOne() request.registry = registry resp = request.response self.assertEqual(resp.__class__, Response) self.assertTrue(request.response is resp) # reified class TestDummyTemplateRenderer(unittest.TestCase): def _getTargetClass(self, ): from pyramid.testing import DummyTemplateRenderer return DummyTemplateRenderer def _makeOne(self, string_response=''): return self._getTargetClass()(string_response=string_response) def test_implementation(self): renderer = self._makeOne() impl = renderer.implementation() impl(a=1, b=2) self.assertEqual(renderer._implementation._received['a'], 1) self.assertEqual(renderer._implementation._received['b'], 2) def test_getattr(self): renderer = self._makeOne() renderer({'a':1}) self.assertEqual(renderer.a, 1) self.assertRaises(AttributeError, renderer.__getattr__, 'b') def test_assert_(self): renderer = self._makeOne() renderer({'a':1, 'b':2}) self.assertRaises(AssertionError, renderer.assert_, c=1) self.assertRaises(AssertionError, renderer.assert_, b=3) self.assertTrue(renderer.assert_(a=1, b=2)) def test_nondefault_string_response(self): renderer = self._makeOne('abc') result = renderer({'a':1, 'b':2}) self.assertEqual(result, 'abc') class Test_setUp(unittest.TestCase): def _callFUT(self, **kw): from pyramid.testing import setUp return setUp(**kw) def tearDown(self): from pyramid.threadlocal import manager manager.clear() getSiteManager = self._getSM() if getSiteManager is not None: getSiteManager.reset() def _getSM(self): try: from zope.component import getSiteManager except ImportError: # pragma: no cover getSiteManager = None return getSiteManager def _assertSMHook(self, hook): getSiteManager = self._getSM() if getSiteManager is not None: result = getSiteManager.sethook(None) self.assertEqual(result, hook) def test_it_defaults(self): from pyramid.threadlocal import manager from pyramid.threadlocal import get_current_registry from pyramid.registry import Registry old = True manager.push(old) config = self._callFUT() current = manager.get() self.assertFalse(current is old) self.assertEqual(config.registry, current['registry']) self.assertEqual(current['registry'].__class__, Registry) self.assertEqual(current['request'], None) self._assertSMHook(get_current_registry) def test_it_with_registry(self): from pyramid.registry import Registry from pyramid.threadlocal import manager registry = Registry() self._callFUT(registry=registry) current = manager.get() self.assertEqual(current['registry'], registry) def test_it_with_request(self): from pyramid.threadlocal import manager request = object() self._callFUT(request=request) current = manager.get() self.assertEqual(current['request'], request) def test_it_with_hook_zca_false(self): from pyramid.registry import Registry registry = Registry() self._callFUT(registry=registry, hook_zca=False) getSiteManager = self._getSM() if getSiteManager is not None: sm = getSiteManager() self.assertFalse(sm is registry) def test_it_with_settings_passed_explicit_registry(self): from pyramid.registry import Registry registry = Registry() self._callFUT(registry=registry, hook_zca=False, settings=dict(a=1)) self.assertEqual(registry.settings['a'], 1) def test_it_with_settings_passed_implicit_registry(self): config = self._callFUT(hook_zca=False, settings=dict(a=1)) self.assertEqual(config.registry.settings['a'], 1) class Test_cleanUp(Test_setUp): def _callFUT(self, *arg, **kw): from pyramid.testing import cleanUp return cleanUp(*arg, **kw) class Test_tearDown(unittest.TestCase): def _callFUT(self, **kw): from pyramid.testing import tearDown return tearDown(**kw) def tearDown(self): from pyramid.threadlocal import manager manager.clear() getSiteManager = self._getSM() if getSiteManager is not None: getSiteManager.reset() def _getSM(self): try: from zope.component import getSiteManager except ImportError: # pragma: no cover getSiteManager = None return getSiteManager def _assertSMHook(self, hook): getSiteManager = self._getSM() if getSiteManager is not None: result = getSiteManager.sethook(None) self.assertEqual(result, hook) def _setSMHook(self, hook): getSiteManager = self._getSM() if getSiteManager is not None: getSiteManager.sethook(hook) def test_defaults(self): from pyramid.threadlocal import manager registry = DummyRegistry() old = {'registry':registry} hook = lambda *arg: None try: self._setSMHook(hook) manager.push(old) self._callFUT() current = manager.get() self.assertNotEqual(current, old) self.assertEqual(registry.inited, 2) finally: getSiteManager = self._getSM() if getSiteManager is not None: result = getSiteManager.sethook(None) self.assertNotEqual(result, hook) def test_registry_cannot_be_inited(self): from pyramid.threadlocal import manager registry = DummyRegistry() def raiseit(name): raise TypeError registry.__init__ = raiseit old = {'registry':registry} try: manager.push(old) self._callFUT() # doesn't blow up current = manager.get() self.assertNotEqual(current, old) self.assertEqual(registry.inited, 1) finally: manager.clear() def test_unhook_zc_false(self): hook = lambda *arg: None try: self._setSMHook(hook) self._callFUT(unhook_zca=False) finally: self._assertSMHook(hook) class TestDummyRendererFactory(unittest.TestCase): def _makeOne(self, name, factory): from pyramid.testing import DummyRendererFactory return DummyRendererFactory(name, factory) def test_add_no_colon(self): f = self._makeOne('name', None) f.add('spec', 'renderer') self.assertEqual(f.renderers['spec'], 'renderer') def test_add_with_colon(self): f = self._makeOne('name', None) f.add('spec:spec2', 'renderer') self.assertEqual(f.renderers['spec:spec2'], 'renderer') self.assertEqual(f.renderers['spec2'], 'renderer') def test_call(self): f = self._makeOne('name', None) f.renderers['spec'] = 'renderer' info = DummyRendererInfo({'name':'spec'}) self.assertEqual(f(info), 'renderer') def test_call2(self): f = self._makeOne('name', None) f.renderers['spec'] = 'renderer' info = DummyRendererInfo({'name':'spec:spec'}) self.assertEqual(f(info), 'renderer') def test_call3(self): def factory(spec): return 'renderer' f = self._makeOne('name', factory) info = DummyRendererInfo({'name':'spec'}) self.assertEqual(f(info), 'renderer') def test_call_miss(self): f = self._makeOne('name', None) info = DummyRendererInfo({'name':'spec'}) self.assertRaises(KeyError, f, info) class TestMockTemplate(unittest.TestCase): def _makeOne(self, response): from pyramid.testing import MockTemplate return MockTemplate(response) def test_getattr(self): template = self._makeOne(None) self.assertEqual(template.foo, template) def test_getitem(self): template = self._makeOne(None) self.assertEqual(template['foo'], template) def test_call(self): template = self._makeOne('123') self.assertEqual(template(), '123') class Test_skip_on(unittest.TestCase): def setUp(self): from pyramid.testing import skip_on self.os_name = skip_on.os_name skip_on.os_name = 'wrong' def tearDown(self): from pyramid.testing import skip_on skip_on.os_name = self.os_name def _callFUT(self, *platforms): from pyramid.testing import skip_on return skip_on(*platforms) def test_wrong_platform(self): def foo(): return True decorated = self._callFUT('wrong')(foo) self.assertEqual(decorated(), None) def test_ok_platform(self): def foo(): return True decorated = self._callFUT('ok')(foo) self.assertEqual(decorated(), True) class TestDummySession(unittest.TestCase): def _makeOne(self): from pyramid.testing import DummySession return DummySession() def test_instance_conforms(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ISession session = self._makeOne() verifyObject(ISession, session) def test_changed(self): session = self._makeOne() self.assertEqual(session.changed(), None) def test_invalidate(self): session = self._makeOne() session['a'] = 1 self.assertEqual(session.invalidate(), None) self.assertFalse('a' in session) def test_flash_default(self): session = self._makeOne() session.flash('msg1') session.flash('msg2') self.assertEqual(session['_f_'], ['msg1', 'msg2']) def test_flash_mixed(self): session = self._makeOne() 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): session = self._makeOne() 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): session = self._makeOne() 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): session = self._makeOne() 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): session = self._makeOne() 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): session = self._makeOne() token = session.new_csrf_token() self.assertEqual(token, session['_csrft_']) def test_get_csrf_token(self): session = self._makeOne() session['_csrft_'] = 'token' token = session.get_csrf_token() self.assertEqual(token, 'token') self.assertTrue('_csrft_' in session) def test_get_csrf_token_generates_token(self): session = self._makeOne() token = session.get_csrf_token() self.assertNotEqual(token, None) self.assertTrue(len(token) >= 1) from zope.interface import Interface from zope.interface import implementer class IDummy(Interface): pass @implementer(IDummy) class DummyEvent: pass class DummyFactory: def __init__(self, environ): """ """ class DummyRegistry(object): inited = 0 __name__ = 'name' def __init__(self, name=''): self.inited = self.inited + 1 class DummyRendererInfo(object): def __init__(self, kw): self.__dict__.update(kw) class Test_testConfig(unittest.TestCase): def _setUp(self, **kw): self._log.append(('setUp', kw)) return 'fake config' def _tearDown(self, **kw): self._log.append(('tearDown', kw)) def setUp(self): from pyramid import testing self._log = [] self._orig_setUp = testing.setUp testing.setUp = self._setUp self._orig_tearDown = testing.tearDown testing.tearDown = self._tearDown def tearDown(self): from pyramid import testing testing.setUp = self._orig_setUp testing.tearDown = self._orig_tearDown def _callFUT(self, inner, **kw): from pyramid.testing import testConfig with testConfig(**kw) as config: inner(config) def test_ok_calls(self): self.assertEqual(self._log, []) def inner(config): self.assertEqual(self._log, [('setUp', {'autocommit': True, 'hook_zca': True, 'registry': None, 'request': None, 'settings': None})]) self._log.pop() self._callFUT(inner) self.assertEqual(self._log, [('tearDown', {'unhook_zca': True})]) def test_teardown_called_on_exception(self): class TestException(Exception): pass def inner(config): self._log = [] raise TestException('oops') self.assertRaises(TestException, self._callFUT, inner) self.assertEqual(self._log[0][0], 'tearDown') def test_ok_get_config(self): def inner(config): self.assertEqual(config, 'fake config') self._callFUT(inner) pyramid-1.4.5/pyramid/tests/fixtures/0000775000175000017500000000000012210157153017137 5ustar takakitakakipyramid-1.4.5/pyramid/tests/fixtures/hellocompo.mak0000664000175000017500000000014512203712502021767 0ustar takakitakaki<%namespace name="comp" file="pyramid.tests:fixtures/components.mak"/> Namespace Hello ${comp.comp()}pyramid-1.4.5/pyramid/tests/fixtures/components.mak0000664000175000017500000000004312203712502022010 0ustar takakitakaki<%def name="comp()"> World! pyramid-1.4.5/pyramid/tests/fixtures/minimal.txt0000664000175000017500000000000711752470445021337 0ustar takakitakakiHello. pyramid-1.4.5/pyramid/tests/fixtures/pp.pt0000664000175000017500000000017711752470445020144 0ustar takakitakaki

WRAPPED

pyramid-1.4.5/pyramid/tests/fixtures/static/0000775000175000017500000000000012210157153020426 5ustar takakitakakipyramid-1.4.5/pyramid/tests/fixtures/static/subdir/0000775000175000017500000000000012210157153021716 5ustar takakitakakipyramid-1.4.5/pyramid/tests/fixtures/static/subdir/index.html0000664000175000017500000000002411752470445023723 0ustar takakitakakisubdir pyramid-1.4.5/pyramid/tests/fixtures/static/arcs.svg.tgz0000664000175000017500000000426412203712502022705 0ustar takakitakaki pyramid-1.4.5/pyramid/tests/fixtures/static/index.html0000664000175000017500000000002312203712502022413 0ustar takakitakakistaticpyramid-1.4.5/pyramid/tests/fixtures/static/.hiddenfile0000664000175000017500000000003112203712502022511 0ustar takakitakakiI'm hidden pyramid-1.4.5/pyramid/tests/fixtures/minimal.pt0000664000175000017500000000014611752470445021147 0ustar takakitakaki
pyramid-1.4.5/pyramid/tests/fixtures/nonminimal.txt0000664000175000017500000000002011752470445022045 0ustar takakitakakiHello, ${name}! pyramid-1.4.5/pyramid/tests/fixtures/helloinherit.mak0000664000175000017500000000005211752470445022330 0ustar takakitakakiHello World! <%inherit file="layout.mak"/>pyramid-1.4.5/pyramid/tests/fixtures/withmacro.pt0000664000175000017500000000011712203712502021475 0ustar takakitakaki Outside macro Hello! pyramid-1.4.5/pyramid/tests/fixtures/helloworld.mako0000664000175000017500000000021312203712502022154 0ustar takakitakaki## -*- coding: utf-8 -*- <%!from pyramid.compat import text_%><% a, b = 'foo', text_('föö', 'utf-8') %> Hello ${text_('föö', 'utf-8')} pyramid-1.4.5/pyramid/tests/fixtures/hello_inherit_pkg.mak0000664000175000017500000000010111752470445023323 0ustar takakitakakiHello World! <%inherit file="pyramid.tests:fixtures/layout.mak"/>pyramid-1.4.5/pyramid/tests/fixtures/layout.mak0000664000175000017500000000002511752470445021157 0ustar takakitakakiLayout ${next.body()}pyramid-1.4.5/pyramid/tests/fixtures/hello .world.mako0000664000175000017500000000021212203712502022271 0ustar takakitakaki## -*- coding: utf-8 -*- <%!from pyramid.compat import text_%><% a, b = 'foo', text_('föö', 'utf-8') %> Hello ${text_('föö', 'utf-8')}pyramid-1.4.5/pyramid/tests/fixtures/nonminimal.mak0000664000175000017500000000002012203711415021761 0ustar takakitakakiHello, ${name}! pyramid-1.4.5/pyramid/tests/fixtures/helloworld.mak0000664000175000017500000000021312203712502021775 0ustar takakitakaki## -*- coding: utf-8 -*- <%!from pyramid.compat import text_%><% a, b = 'foo', text_('föö', 'utf-8') %> Hello ${text_('föö', 'utf-8')} pyramid-1.4.5/pyramid/tests/test_paster.py0000664000175000017500000001464212210154276020207 0ustar takakitakakiimport os import unittest class Test_get_app(unittest.TestCase): def _callFUT(self, config_file, section_name, options=None, loadapp=None): from pyramid.paster import get_app return get_app( config_file, section_name, options=options, loadapp=loadapp ) def test_it(self): app = DummyApp() loadapp = DummyLoadWSGI(app) result = self._callFUT('/foo/bar/myapp.ini', 'myapp', loadapp=loadapp) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'myapp') self.assertEqual(loadapp.relative_to, os.getcwd()) self.assertEqual(result, app) def test_it_with_hash(self): app = DummyApp() loadapp = DummyLoadWSGI(app) result = self._callFUT( '/foo/bar/myapp.ini#myapp', None, loadapp=loadapp ) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'myapp') self.assertEqual(loadapp.relative_to, os.getcwd()) self.assertEqual(result, app) def test_it_with_hash_and_name_override(self): app = DummyApp() loadapp = DummyLoadWSGI(app) result = self._callFUT( '/foo/bar/myapp.ini#myapp', 'yourapp', loadapp=loadapp ) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'yourapp') self.assertEqual(loadapp.relative_to, os.getcwd()) self.assertEqual(result, app) def test_it_with_options(self): app = DummyApp() loadapp = DummyLoadWSGI(app) options = {'a':1} result = self._callFUT( '/foo/bar/myapp.ini#myapp', 'yourapp', loadapp=loadapp, options=options, ) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'yourapp') self.assertEqual(loadapp.relative_to, os.getcwd()) self.assertEqual(loadapp.kw, {'global_conf':options}) self.assertEqual(result, app) class Test_get_appsettings(unittest.TestCase): def _callFUT(self, config_file, section_name, appconfig): from pyramid.paster import get_appsettings return get_appsettings(config_file, section_name, appconfig) def test_it(self): values = {'a':1} appconfig = DummyLoadWSGI(values) result = self._callFUT('/foo/bar/myapp.ini', 'myapp', appconfig) self.assertEqual(appconfig.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(appconfig.section_name, 'myapp') self.assertEqual(appconfig.relative_to, os.getcwd()) self.assertEqual(result, values) def test_it_with_hash(self): values = {'a':1} appconfig = DummyLoadWSGI(values) result = self._callFUT('/foo/bar/myapp.ini#myapp', None, appconfig) self.assertEqual(appconfig.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(appconfig.section_name, 'myapp') self.assertEqual(appconfig.relative_to, os.getcwd()) self.assertEqual(result, values) def test_it_with_hash_and_name_override(self): values = {'a':1} appconfig = DummyLoadWSGI(values) result = self._callFUT('/foo/bar/myapp.ini#myapp', 'yourapp', appconfig) self.assertEqual(appconfig.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(appconfig.section_name, 'yourapp') self.assertEqual(appconfig.relative_to, os.getcwd()) self.assertEqual(result, values) class Test_setup_logging(unittest.TestCase): def _callFUT(self, config_file): from pyramid.paster import setup_logging dummy_cp = DummyConfigParserModule return setup_logging(config_file, self.fileConfig, dummy_cp) def test_it(self): config_file, dict = self._callFUT('/abc') # os.path.abspath 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 Test_bootstrap(unittest.TestCase): def _callFUT(self, config_uri, request=None): from pyramid.paster import bootstrap return bootstrap(config_uri, request) def setUp(self): import pyramid.paster self.original_get_app = pyramid.paster.get_app self.original_prepare = pyramid.paster.prepare self.app = app = DummyApp() self.root = root = Dummy() class DummyGetApp(object): def __call__(self, *a, **kw): self.a = a self.kw = kw return app self.get_app = pyramid.paster.get_app = DummyGetApp() class DummyPrepare(object): def __call__(self, *a, **kw): self.a = a self.kw = kw return {'root':root, 'closer':lambda: None} self.getroot = pyramid.paster.prepare = DummyPrepare() def tearDown(self): import pyramid.paster pyramid.paster.get_app = self.original_get_app pyramid.paster.prepare = self.original_prepare def test_it_request_with_registry(self): request = DummyRequest({}) request.registry = dummy_registry result = self._callFUT('/foo/bar/myapp.ini', request) self.assertEqual(result['app'], self.app) self.assertEqual(result['root'], self.root) self.assertTrue('closer' in result) class Dummy: pass class DummyRegistry(object): settings = {} dummy_registry = DummyRegistry() class DummyLoadWSGI: def __init__(self, result): self.result = result def __call__(self, config_name, name=None, relative_to=None, **kw): self.config_name = config_name self.section_name = name self.relative_to = relative_to self.kw = kw return self.result class DummyApp: def __init__(self): self.registry = dummy_registry class DummyRequest: application_url = 'http://example.com:5432' script_name = '' def __init__(self, environ): self.environ = environ self.matchdict = {} 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_registry.py0000664000175000017500000003052512203712502020551 0ustar takakitakakiimport unittest class TestRegistry(unittest.TestCase): def _getTargetClass(self): from pyramid.registry import Registry return Registry def _makeOne(self): return self._getTargetClass()() def test___nonzero__(self): registry = self._makeOne() self.assertEqual(registry.__nonzero__(), True) def test_registerHandler_and_notify(self): registry = self._makeOne() self.assertEqual(registry.has_listeners, False) L = [] def f(event): L.append(event) registry.registerHandler(f, [IDummyEvent]) self.assertEqual(registry.has_listeners, True) event = DummyEvent() registry.notify(event) self.assertEqual(L, [event]) def test_registerSubscriptionAdapter(self): registry = self._makeOne() self.assertEqual(registry.has_listeners, False) from zope.interface import Interface registry.registerSubscriptionAdapter(DummyEvent, [IDummyEvent], Interface) self.assertEqual(registry.has_listeners, True) def test__get_settings(self): registry = self._makeOne() registry._settings = 'foo' self.assertEqual(registry.settings, 'foo') def test__set_settings(self): registry = self._makeOne() registry.settings = 'foo' self.assertEqual(registry._settings, 'foo') class TestIntrospector(unittest.TestCase): def _getTargetClass(slf): from pyramid.registry import Introspector return Introspector def _makeOne(self): return self._getTargetClass()() def test_conformance(self): from zope.interface.verify import verifyClass from zope.interface.verify import verifyObject from pyramid.interfaces import IIntrospector verifyClass(IIntrospector, self._getTargetClass()) verifyObject(IIntrospector, self._makeOne()) def test_add(self): inst = self._makeOne() intr = DummyIntrospectable() inst.add(intr) self.assertEqual(intr.order, 0) category = {'discriminator':intr, 'discriminator_hash':intr} self.assertEqual(inst._categories, {'category':category}) def test_get_success(self): inst = self._makeOne() intr = DummyIntrospectable() inst.add(intr) self.assertEqual(inst.get('category', 'discriminator'), intr) def test_get_success_byhash(self): inst = self._makeOne() intr = DummyIntrospectable() inst.add(intr) self.assertEqual(inst.get('category', 'discriminator_hash'), intr) def test_get_fail(self): inst = self._makeOne() intr = DummyIntrospectable() inst.add(intr) self.assertEqual(inst.get('category', 'wontexist', 'foo'), 'foo') def test_get_category(self): inst = self._makeOne() intr = DummyIntrospectable() intr2 = DummyIntrospectable() intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' inst.add(intr2) inst.add(intr) expected = [ {'introspectable':intr2, 'related':[]}, {'introspectable':intr, 'related':[]}, ] self.assertEqual(inst.get_category('category'), expected) def test_get_category_returns_default_on_miss(self): inst = self._makeOne() self.assertEqual(inst.get_category('category', '123'), '123') def test_get_category_with_sortkey(self): import operator inst = self._makeOne() intr = DummyIntrospectable() intr.foo = 2 intr2 = DummyIntrospectable() intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' intr2.foo = 1 inst.add(intr) inst.add(intr2) expected = [ {'introspectable':intr2, 'related':[]}, {'introspectable':intr, 'related':[]}, ] self.assertEqual( inst.get_category('category', sort_key=operator.attrgetter('foo')), expected) def test_categorized(self): import operator inst = self._makeOne() intr = DummyIntrospectable() intr.foo = 2 intr2 = DummyIntrospectable() intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' intr2.foo = 1 inst.add(intr) inst.add(intr2) expected = [('category', [ {'introspectable':intr2, 'related':[]}, {'introspectable':intr, 'related':[]}, ])] self.assertEqual( inst.categorized(sort_key=operator.attrgetter('foo')), expected) def test_categories(self): inst = self._makeOne() inst._categories['a'] = 1 inst._categories['b'] = 2 self.assertEqual(list(inst.categories()), ['a', 'b']) def test_remove(self): inst = self._makeOne() intr = DummyIntrospectable() intr2 = DummyIntrospectable() intr2.category_name = 'category2' intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' inst.add(intr) inst.add(intr2) inst.relate(('category', 'discriminator'), ('category2', 'discriminator2')) inst.remove('category', 'discriminator') self.assertEqual(inst._categories, {'category': {}, 'category2': {'discriminator2': intr2, 'discriminator2_hash': intr2} }) self.assertEqual(inst._refs.get(intr), None) self.assertEqual(inst._refs[intr2], []) def test_remove_fail(self): inst = self._makeOne() self.assertEqual(inst.remove('a', 'b'), None) def test_relate(self): inst = self._makeOne() intr = DummyIntrospectable() intr2 = DummyIntrospectable() intr2.category_name = 'category2' intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' inst.add(intr) inst.add(intr2) inst.relate(('category', 'discriminator'), ('category2', 'discriminator2')) self.assertEqual(inst._categories, {'category': {'discriminator':intr, 'discriminator_hash':intr}, 'category2': {'discriminator2': intr2, 'discriminator2_hash': intr2} }) self.assertEqual(inst._refs[intr], [intr2]) self.assertEqual(inst._refs[intr2], [intr]) def test_relate_fail(self): inst = self._makeOne() intr = DummyIntrospectable() inst.add(intr) self.assertRaises( KeyError, inst.relate, ('category', 'discriminator'), ('category2', 'discriminator2') ) def test_unrelate(self): inst = self._makeOne() intr = DummyIntrospectable() intr2 = DummyIntrospectable() intr2.category_name = 'category2' intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' inst.add(intr) inst.add(intr2) inst.relate(('category', 'discriminator'), ('category2', 'discriminator2')) inst.unrelate(('category', 'discriminator'), ('category2', 'discriminator2')) self.assertEqual(inst._categories, {'category': {'discriminator':intr, 'discriminator_hash':intr}, 'category2': {'discriminator2': intr2, 'discriminator2_hash': intr2} }) self.assertEqual(inst._refs[intr], []) self.assertEqual(inst._refs[intr2], []) def test_related(self): inst = self._makeOne() intr = DummyIntrospectable() intr2 = DummyIntrospectable() intr2.category_name = 'category2' intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' inst.add(intr) inst.add(intr2) inst.relate(('category', 'discriminator'), ('category2', 'discriminator2')) self.assertEqual(inst.related(intr), [intr2]) def test_related_fail(self): inst = self._makeOne() intr = DummyIntrospectable() intr2 = DummyIntrospectable() intr2.category_name = 'category2' intr2.discriminator = 'discriminator2' intr2.discriminator_hash = 'discriminator2_hash' inst.add(intr) inst.add(intr2) inst.relate(('category', 'discriminator'), ('category2', 'discriminator2')) del inst._categories['category'] self.assertRaises(KeyError, inst.related, intr) class TestIntrospectable(unittest.TestCase): def _getTargetClass(slf): from pyramid.registry import Introspectable return Introspectable def _makeOne(self, *arg, **kw): return self._getTargetClass()(*arg, **kw) def _makeOnePopulated(self): return self._makeOne('category', 'discrim', 'title', 'type') def test_conformance(self): from zope.interface.verify import verifyClass from zope.interface.verify import verifyObject from pyramid.interfaces import IIntrospectable verifyClass(IIntrospectable, self._getTargetClass()) verifyObject(IIntrospectable, self._makeOnePopulated()) def test_relate(self): inst = self._makeOnePopulated() inst.relate('a', 'b') self.assertEqual(inst._relations, [(True, 'a', 'b')]) def test_unrelate(self): inst = self._makeOnePopulated() inst.unrelate('a', 'b') self.assertEqual(inst._relations, [(False, 'a', 'b')]) def test_discriminator_hash(self): inst = self._makeOnePopulated() self.assertEqual(inst.discriminator_hash, hash(inst.discriminator)) def test___hash__(self): inst = self._makeOnePopulated() self.assertEqual(hash(inst), hash((inst.category_name,) + (inst.discriminator,))) def test___repr__(self): inst = self._makeOnePopulated() self.assertEqual( repr(inst), "") def test___nonzero__(self): inst = self._makeOnePopulated() self.assertEqual(inst.__nonzero__(), True) def test___bool__(self): inst = self._makeOnePopulated() self.assertEqual(inst.__bool__(), True) def test_register(self): introspector = DummyIntrospector() action_info = object() inst = self._makeOnePopulated() inst._relations.append((True, 'category1', 'discrim1')) inst._relations.append((False, 'category2', 'discrim2')) inst.register(introspector, action_info) self.assertEqual(inst.action_info, action_info) self.assertEqual(introspector.intrs, [inst]) self.assertEqual(introspector.relations, [(('category', 'discrim'), ('category1', 'discrim1'))]) self.assertEqual(introspector.unrelations, [(('category', 'discrim'), ('category2', 'discrim2'))]) class DummyIntrospector(object): def __init__(self): self.intrs = [] self.relations = [] self.unrelations = [] def add(self, intr): self.intrs.append(intr) def relate(self, *pairs): self.relations.append(pairs) def unrelate(self, *pairs): self.unrelations.append(pairs) class DummyModule: __path__ = "foo" __name__ = "dummy" __file__ = '' class DummyIntrospectable(object): category_name = 'category' discriminator = 'discriminator' title = 'title' type_name = 'type' order = None action_info = None discriminator_hash = 'discriminator_hash' def __hash__(self): return hash((self.category_name,) + (self.discriminator,)) from zope.interface import Interface from zope.interface import implementer class IDummyEvent(Interface): pass @implementer(IDummyEvent) class DummyEvent(object): pass pyramid-1.4.5/pyramid/tests/test_httpexceptions.py0000664000175000017500000003213012210154301021747 0ustar takakitakakiimport unittest from pyramid.compat import ( bytes_, text_, ) class Test_exception_response(unittest.TestCase): def _callFUT(self, *arg, **kw): from pyramid.httpexceptions import exception_response return exception_response(*arg, **kw) def test_status_404(self): from pyramid.httpexceptions import HTTPNotFound self.assertEqual(self._callFUT(404).__class__, HTTPNotFound) def test_status_201(self): from pyramid.httpexceptions import HTTPCreated self.assertEqual(self._callFUT(201).__class__, HTTPCreated) def test_extra_kw(self): resp = self._callFUT(404, headers=[('abc', 'def')]) self.assertEqual(resp.headers['abc'], 'def') class Test_default_exceptionresponse_view(unittest.TestCase): def _callFUT(self, context, request): from pyramid.httpexceptions import default_exceptionresponse_view return default_exceptionresponse_view(context, request) def test_call_with_exception(self): context = Exception() result = self._callFUT(context, None) self.assertEqual(result, context) def test_call_with_nonexception(self): request = DummyRequest() context = Exception() request.exception = context result = self._callFUT(None, request) self.assertEqual(result, context) class Test__no_escape(unittest.TestCase): def _callFUT(self, val): from pyramid.httpexceptions import _no_escape return _no_escape(val) def test_null(self): self.assertEqual(self._callFUT(None), '') def test_not_basestring(self): self.assertEqual(self._callFUT(42), '42') def test_unicode(self): class DummyUnicodeObject(object): def __unicode__(self): return text_('42') duo = DummyUnicodeObject() self.assertEqual(self._callFUT(duo), text_('42')) class TestHTTPException(unittest.TestCase): def _getTargetClass(self): from pyramid.httpexceptions import HTTPException return HTTPException def _getTargetSubclass(self, code='200', title='OK', explanation='explanation', empty_body=False): cls = self._getTargetClass() class Subclass(cls): pass Subclass.empty_body = empty_body Subclass.code = code Subclass.title = title Subclass.explanation = explanation return Subclass def _makeOne(self, *arg, **kw): cls = self._getTargetClass() return cls(*arg, **kw) def test_implements_IResponse(self): from pyramid.interfaces import IResponse cls = self._getTargetClass() self.assertTrue(IResponse.implementedBy(cls)) def test_provides_IResponse(self): from pyramid.interfaces import IResponse inst = self._getTargetClass()() self.assertTrue(IResponse.providedBy(inst)) def test_implements_IExceptionResponse(self): from pyramid.interfaces import IExceptionResponse cls = self._getTargetClass() self.assertTrue(IExceptionResponse.implementedBy(cls)) def test_provides_IExceptionResponse(self): from pyramid.interfaces import IExceptionResponse inst = self._getTargetClass()() self.assertTrue(IExceptionResponse.providedBy(inst)) def test_ctor_sets_detail(self): exc = self._makeOne('message') self.assertEqual(exc.detail, 'message') def test_ctor_sets_comment(self): exc = self._makeOne(comment='comment') self.assertEqual(exc.comment, 'comment') def test_ctor_calls_Exception_ctor(self): exc = self._makeOne('message') self.assertEqual(exc.message, 'message') def test_ctor_calls_Response_ctor(self): exc = self._makeOne('message') self.assertEqual(exc.status, 'None None') def test_ctor_extends_headers(self): exc = self._makeOne(headers=[('X-Foo', 'foo')]) self.assertEqual(exc.headers.get('X-Foo'), 'foo') def test_ctor_sets_body_template_obj(self): exc = self._makeOne(body_template='${foo}') self.assertEqual( exc.body_template_obj.substitute({'foo':'foo'}), 'foo') def test_ctor_with_empty_body(self): cls = self._getTargetSubclass(empty_body=True) exc = cls() self.assertEqual(exc.content_type, None) self.assertEqual(exc.content_length, None) def test_ctor_with_body_doesnt_set_default_app_iter(self): exc = self._makeOne(body=b'123') self.assertEqual(exc.app_iter, [b'123']) def test_ctor_with_unicode_body_doesnt_set_default_app_iter(self): exc = self._makeOne(unicode_body=text_('123')) self.assertEqual(exc.app_iter, [b'123']) def test_ctor_with_app_iter_doesnt_set_default_app_iter(self): exc = self._makeOne(app_iter=[b'123']) self.assertEqual(exc.app_iter, [b'123']) def test_ctor_with_body_sets_default_app_iter_html(self): cls = self._getTargetSubclass() exc = cls('detail') environ = _makeEnviron() environ['HTTP_ACCEPT'] = 'text/html' start_response = DummyStartResponse() body = list(exc(environ, start_response))[0] self.assertTrue(body.startswith(b'' in body) def test__default_app_iter_with_comment_html2(self): cls = self._getTargetSubclass() exc = cls(comment='comment & comment') environ = _makeEnviron() environ['HTTP_ACCEPT'] = 'text/html' start_response = DummyStartResponse() body = list(exc(environ, start_response))[0] self.assertTrue(b'' in body) def test_custom_body_template(self): cls = self._getTargetSubclass() exc = cls(body_template='${REQUEST_METHOD}') environ = _makeEnviron() start_response = DummyStartResponse() body = list(exc(environ, start_response))[0] self.assertEqual(body, b'200 OK\n\nGET') def test_custom_body_template_with_custom_variable_doesnt_choke(self): cls = self._getTargetSubclass() exc = cls(body_template='${REQUEST_METHOD}') environ = _makeEnviron() class Choke(object): def __str__(self): raise ValueError environ['gardentheory.user'] = Choke() start_response = DummyStartResponse() body = list(exc(environ, start_response))[0] self.assertEqual(body, b'200 OK\n\nGET') def test_body_template_unicode(self): cls = self._getTargetSubclass() la = text_(b'/La Pe\xc3\xb1a', 'utf-8') environ = _makeEnviron(unicodeval=la) exc = cls(body_template='${unicodeval}') start_response = DummyStartResponse() body = list(exc(environ, start_response))[0] self.assertEqual(body, b'200 OK\n\n/La Pe\xc3\xb1a') class TestRenderAllExceptionsWithoutArguments(unittest.TestCase): def _doit(self, content_type): from pyramid.httpexceptions import status_map L = [] self.assertTrue(status_map) for v in status_map.values(): environ = _makeEnviron() start_response = DummyStartResponse() exc = v() exc.content_type = content_type result = list(exc(environ, start_response))[0] if exc.empty_body: self.assertEqual(result, b'') else: self.assertTrue(bytes_(exc.status) in result) L.append(result) self.assertEqual(len(L), len(status_map)) def test_it_plain(self): self._doit('text/plain') def test_it_html(self): self._doit('text/html') class Test_HTTPMove(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.httpexceptions import _HTTPMove return _HTTPMove(*arg, **kw) def test_it_location_none_valueerrors(self): # Constructing a HTTPMove instance with location=None should # throw a ValueError from __init__ so that a more-confusing # exception won't be thrown later from .prepare(environ) self.assertRaises(ValueError, self._makeOne, location=None) def test_it_location_not_passed(self): exc = self._makeOne() self.assertEqual(exc.location, '') def test_it_location_passed(self): exc = self._makeOne(location='foo') self.assertEqual(exc.location, 'foo') def test_it_location_firstarg(self): exc = self._makeOne('foo') self.assertEqual(exc.location, 'foo') def test_it_call_with_default_body_tmpl(self): exc = self._makeOne(location='foo') environ = _makeEnviron() start_response = DummyStartResponse() app_iter = exc(environ, start_response) self.assertEqual(app_iter[0], (b'None None\n\nThe resource has been moved to foo; ' b'you should be redirected automatically.\n\n')) class TestHTTPForbidden(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.httpexceptions import HTTPForbidden return HTTPForbidden(*arg, **kw) def test_it_result_not_passed(self): exc = self._makeOne() self.assertEqual(exc.result, None) def test_it_result_passed(self): exc = self._makeOne(result='foo') self.assertEqual(exc.result, 'foo') class TestHTTPMethodNotAllowed(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.httpexceptions import HTTPMethodNotAllowed return HTTPMethodNotAllowed(*arg, **kw) def test_it_with_default_body_tmpl(self): exc = self._makeOne() environ = _makeEnviron() start_response = DummyStartResponse() app_iter = exc(environ, start_response) self.assertEqual(app_iter[0], (b'405 Method Not Allowed\n\nThe method GET is not ' b'allowed for this resource. \n\n\n')) class DummyRequest(object): exception = None class DummyStartResponse(object): def __call__(self, status, headerlist): self.status = status self.headerlist = headerlist def _makeEnviron(**kw): environ = {'REQUEST_METHOD':'GET', 'wsgi.url_scheme':'http', 'SERVER_NAME':'localhost', 'SERVER_PORT':'80'} environ.update(kw) return environ pyramid-1.4.5/pyramid/tests/__init__.py0000664000175000017500000000010511752470445017407 0ustar takakitakaki def dummy_extend(*args): """used to test Configurator.extend""" pyramid-1.4.5/pyramid/tests/test_router.py0000664000175000017500000015222512210154276020231 0ustar takakitakakiimport unittest from pyramid import testing class TestRouter(unittest.TestCase): def setUp(self): self.config = testing.setUp() self.registry = self.config.registry def tearDown(self): testing.tearDown() def _registerRouteRequest(self, name): from pyramid.interfaces import IRouteRequest from pyramid.request import route_request_iface iface = route_request_iface(name) self.registry.registerUtility(iface, IRouteRequest, name=name) return iface def _connectRoute(self, name, path, factory=None): from pyramid.interfaces import IRoutesMapper from pyramid.urldispatch import RoutesMapper mapper = self.registry.queryUtility(IRoutesMapper) if mapper is None: mapper = RoutesMapper() self.registry.registerUtility(mapper, IRoutesMapper) return mapper.connect(name, path, factory) def _registerLogger(self): from pyramid.interfaces import IDebugLogger logger = DummyLogger() self.registry.registerUtility(logger, IDebugLogger) return logger def _registerSettings(self, **kw): settings = {'debug_authorization':False, 'debug_notfound':False, 'debug_routematch':False} settings.update(kw) self.registry.settings = settings def _registerTraverserFactory(self, context, view_name='', subpath=None, traversed=None, virtual_root=None, virtual_root_path=None, raise_error=None, **kw): from pyramid.interfaces import ITraverser if virtual_root is None: virtual_root = context if subpath is None: subpath = [] if traversed is None: traversed = [] if virtual_root_path is None: virtual_root_path = [] class DummyTraverserFactory: def __init__(self, root): self.root = root def __call__(self, request): if raise_error: raise raise_error values = {'root':self.root, 'context':context, 'view_name':view_name, 'subpath':subpath, 'traversed':traversed, 'virtual_root':virtual_root, 'virtual_root_path':virtual_root_path} kw.update(values) return kw self.registry.registerAdapter(DummyTraverserFactory, (None,), ITraverser, name='') def _registerView(self, app, name, classifier, req_iface, ctx_iface): from pyramid.interfaces import IView self.registry.registerAdapter( app, (classifier, req_iface, ctx_iface), IView, name) def _registerEventListener(self, iface): L = [] def listener(event): L.append(event) self.registry.registerHandler(listener, (iface,)) return L def _registerRootFactory(self, val): rootfactory = DummyRootFactory(val) from pyramid.interfaces import IRootFactory self.registry.registerUtility(rootfactory, IRootFactory) return rootfactory def _getTargetClass(self): from pyramid.router import Router return Router def _makeOne(self): klass = self._getTargetClass() return klass(self.registry) def _makeEnviron(self, **extras): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'localhost', 'SERVER_PORT':'8080', 'REQUEST_METHOD':'GET', 'PATH_INFO':'/', } environ.update(extras) return environ def test_ctor_registry_has_no_settings(self): self.registry.settings = None router = self._makeOne() self.assertEqual(router.debug_notfound, False) self.assertEqual(router.debug_routematch, False) self.assertFalse('debug_notfound' in router.__dict__) self.assertFalse('debug_routematch' in router.__dict__) def test_root_policy(self): context = DummyContext() self._registerTraverserFactory(context) rootfactory = self._registerRootFactory('abc') router = self._makeOne() self.assertEqual(router.root_policy, rootfactory) def test_request_factory(self): from pyramid.interfaces import IRequestFactory class DummyRequestFactory(object): pass self.registry.registerUtility(DummyRequestFactory, IRequestFactory) router = self._makeOne() self.assertEqual(router.request_factory, DummyRequestFactory) def test_tween_factories(self): from pyramid.interfaces import ITweens from pyramid.config.tweens import Tweens from pyramid.response import Response from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IResponse tweens = Tweens() self.registry.registerUtility(tweens, ITweens) L = [] def tween_factory1(handler, registry): L.append((handler, registry)) def wrapper(request): request.environ['handled'].append('one') return handler(request) wrapper.name = 'one' wrapper.child = handler return wrapper def tween_factory2(handler, registry): L.append((handler, registry)) def wrapper(request): request.environ['handled'] = ['two'] return handler(request) wrapper.name = 'two' wrapper.child = handler return wrapper tweens.add_implicit('one', tween_factory1) tweens.add_implicit('two', tween_factory2) router = self._makeOne() self.assertEqual(router.handle_request.name, 'two') self.assertEqual(router.handle_request.child.name, 'one') self.assertEqual(router.handle_request.child.child.__name__, 'handle_request') context = DummyContext() self._registerTraverserFactory(context) environ = self._makeEnviron() view = DummyView('abc') self._registerView(self.config.derive_view(view), '', IViewClassifier, None, None) start_response = DummyStartResponse() def make_response(s): return Response(s) router.registry.registerAdapter(make_response, (str,), IResponse) app_iter = router(environ, start_response) self.assertEqual(app_iter, [b'abc']) self.assertEqual(start_response.status, '200 OK') self.assertEqual(environ['handled'], ['two', 'one']) def test_call_traverser_default(self): from pyramid.httpexceptions import HTTPNotFound environ = self._makeEnviron() logger = self._registerLogger() router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertTrue('/' in why.args[0], why) self.assertFalse('debug_notfound' in why.args[0]) self.assertEqual(len(logger.messages), 0) def test_traverser_raises_notfound_class(self): from pyramid.httpexceptions import HTTPNotFound environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context, raise_error=HTTPNotFound) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(HTTPNotFound, router, environ, start_response) def test_traverser_raises_notfound_instance(self): from pyramid.httpexceptions import HTTPNotFound environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context, raise_error=HTTPNotFound('foo')) router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertTrue('foo' in why.args[0], why) def test_traverser_raises_forbidden_class(self): from pyramid.httpexceptions import HTTPForbidden environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context, raise_error=HTTPForbidden) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(HTTPForbidden, router, environ, start_response) def test_traverser_raises_forbidden_instance(self): from pyramid.httpexceptions import HTTPForbidden environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context, raise_error=HTTPForbidden('foo')) router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPForbidden, router, environ, start_response) self.assertTrue('foo' in why.args[0], why) def test_call_no_view_registered_no_isettings(self): from pyramid.httpexceptions import HTTPNotFound environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context) logger = self._registerLogger() router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertTrue('/' in why.args[0], why) self.assertFalse('debug_notfound' in why.args[0]) self.assertEqual(len(logger.messages), 0) def test_call_no_view_registered_debug_notfound_false(self): from pyramid.httpexceptions import HTTPNotFound environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context) logger = self._registerLogger() self._registerSettings(debug_notfound=False) router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertTrue('/' in why.args[0], why) self.assertFalse('debug_notfound' in why.args[0]) self.assertEqual(len(logger.messages), 0) def test_call_no_view_registered_debug_notfound_true(self): from pyramid.httpexceptions import HTTPNotFound environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context) self._registerSettings(debug_notfound=True) logger = self._registerLogger() router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertTrue( "debug_notfound of url http://localhost:8080/; " in why.args[0]) self.assertTrue("view_name: '', subpath: []" in why.args[0]) self.assertTrue('http://localhost:8080' in why.args[0], why) self.assertEqual(len(logger.messages), 1) message = logger.messages[0] self.assertTrue('of url http://localhost:8080' in message) self.assertTrue("path_info: " in message) self.assertTrue('DummyContext' in message) self.assertTrue("view_name: ''" in message) self.assertTrue("subpath: []" in message) def test_call_view_returns_non_iresponse(self): from pyramid.interfaces import IViewClassifier context = DummyContext() self._registerTraverserFactory(context) environ = self._makeEnviron() view = DummyView('abc') self._registerView(self.config.derive_view(view), '', IViewClassifier, None, None) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(ValueError, router, environ, start_response) def test_call_view_returns_adapted_response(self): from pyramid.response import Response from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IResponse context = DummyContext() self._registerTraverserFactory(context) environ = self._makeEnviron() view = DummyView('abc') self._registerView(self.config.derive_view(view), '', IViewClassifier, None, None) router = self._makeOne() start_response = DummyStartResponse() def make_response(s): return Response(s) router.registry.registerAdapter(make_response, (str,), IResponse) app_iter = router(environ, start_response) self.assertEqual(app_iter, [b'abc']) self.assertEqual(start_response.status, '200 OK') def test_call_with_request_extensions(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRequestExtensions from pyramid.interfaces import IRequest from pyramid.request import Request context = DummyContext() self._registerTraverserFactory(context) class Extensions(object): def __init__(self): self.methods = {} self.descriptors = {} extensions = Extensions() L = [] request = Request.blank('/') request.request_iface = IRequest request.registry = self.registry request._set_extensions = lambda *x: L.extend(x) def request_factory(environ): return request self.registry.registerUtility(extensions, IRequestExtensions) environ = self._makeEnviron() response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) self._registerView(self.config.derive_view(view), '', IViewClassifier, None, None) router = self._makeOne() router.request_factory = request_factory start_response = DummyStartResponse() router(environ, start_response) self.assertEqual(L, [extensions]) def test_call_view_registered_nonspecific_default_path(self): from pyramid.interfaces import IViewClassifier context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron() self._registerView(self.config.derive_view(view), '', IViewClassifier, None, None) self._registerRootFactory(context) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ['Hello world']) self.assertEqual(start_response.headers, ()) self.assertEqual(start_response.status, '200 OK') request = view.request self.assertEqual(request.view_name, '') self.assertEqual(request.subpath, []) self.assertEqual(request.context, context) self.assertEqual(request.root, context) def test_call_view_registered_nonspecific_nondefault_path_and_subpath(self): from pyramid.interfaces import IViewClassifier context = DummyContext() self._registerTraverserFactory(context, view_name='foo', subpath=['bar'], traversed=['context']) self._registerRootFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron() self._registerView(view, 'foo', IViewClassifier, None, None) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ['Hello world']) self.assertEqual(start_response.headers, ()) self.assertEqual(start_response.status, '200 OK') request = view.request self.assertEqual(request.view_name, 'foo') self.assertEqual(request.subpath, ['bar']) self.assertEqual(request.context, context) self.assertEqual(request.root, context) def test_call_view_registered_specific_success(self): from zope.interface import Interface from zope.interface import directlyProvides class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context) self._registerRootFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ['Hello world']) self.assertEqual(start_response.headers, ()) self.assertEqual(start_response.status, '200 OK') request = view.request self.assertEqual(request.view_name, '') self.assertEqual(request.subpath, []) self.assertEqual(request.context, context) self.assertEqual(request.root, context) def test_call_view_registered_specific_fail(self): from zope.interface import Interface from zope.interface import directlyProvides from pyramid.httpexceptions import HTTPNotFound from pyramid.interfaces import IViewClassifier class IContext(Interface): pass class INotContext(Interface): pass from pyramid.interfaces import IRequest context = DummyContext() directlyProvides(context, INotContext) self._registerTraverserFactory(context, subpath=['']) response = DummyResponse() view = DummyView(response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(HTTPNotFound, router, environ, start_response) def test_call_view_raises_forbidden(self): from zope.interface import Interface from zope.interface import directlyProvides from pyramid.httpexceptions import HTTPForbidden class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) response = DummyResponse() view = DummyView(response, raise_exception=HTTPForbidden("unauthorized")) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPForbidden, router, environ, start_response) self.assertEqual(why.args[0], 'unauthorized') def test_call_view_raises_notfound(self): from zope.interface import Interface from zope.interface import directlyProvides class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier from pyramid.httpexceptions import HTTPNotFound context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) response = DummyResponse() view = DummyView(response, raise_exception=HTTPNotFound("notfound")) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertEqual(why.args[0], 'notfound') def test_call_view_raises_response_cleared(self): from zope.interface import Interface from zope.interface import directlyProvides from pyramid.interfaces import IExceptionViewClassifier class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) def view(context, request): request.response.a = 1 raise KeyError def exc_view(context, request): self.assertFalse(hasattr(request.response, 'a')) request.response.body = b'OK' return request.response environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) self._registerView(exc_view, '', IExceptionViewClassifier, IRequest, KeyError) router = self._makeOne() start_response = DummyStartResponse() itera = router(environ, start_response) self.assertEqual(itera, [b'OK']) def test_call_request_has_response_callbacks(self): from zope.interface import Interface from zope.interface import directlyProvides class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) response = DummyResponse('200 OK') def view(context, request): def callback(request, response): response.called_back = True request.response_callbacks = [callback] return response environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() router(environ, start_response) self.assertEqual(response.called_back, True) def test_call_request_has_finished_callbacks_when_view_succeeds(self): from zope.interface import Interface from zope.interface import directlyProvides class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) response = DummyResponse('200 OK') def view(context, request): def callback(request): request.environ['called_back'] = True request.finished_callbacks = [callback] return response environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() router(environ, start_response) self.assertEqual(environ['called_back'], True) def test_call_request_has_finished_callbacks_when_view_raises(self): from zope.interface import Interface from zope.interface import directlyProvides class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) def view(context, request): def callback(request): request.environ['called_back'] = True request.finished_callbacks = [callback] raise NotImplementedError environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, IContext) router = self._makeOne() start_response = DummyStartResponse() exc_raised(NotImplementedError, router, environ, start_response) self.assertEqual(environ['called_back'], True) def test_call_request_factory_raises(self): # making sure finally doesnt barf when a request cannot be created environ = self._makeEnviron() router = self._makeOne() def dummy_request_factory(environ): raise NotImplementedError router.request_factory = dummy_request_factory start_response = DummyStartResponse() exc_raised(NotImplementedError, router, environ, start_response) def test_call_eventsends(self): from pyramid.interfaces import INewRequest from pyramid.interfaces import INewResponse from pyramid.interfaces import IContextFound from pyramid.interfaces import IViewClassifier context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, None, None) request_events = self._registerEventListener(INewRequest) aftertraversal_events = self._registerEventListener(IContextFound) response_events = self._registerEventListener(INewResponse) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(len(request_events), 1) self.assertEqual(request_events[0].request.environ, environ) self.assertEqual(len(aftertraversal_events), 1) self.assertEqual(aftertraversal_events[0].request.environ, environ) self.assertEqual(len(response_events), 1) self.assertEqual(response_events[0].response, response) self.assertEqual(result, response.app_iter) def test_call_newrequest_evllist_exc_can_be_caught_by_exceptionview(self): from pyramid.interfaces import INewRequest from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest context = DummyContext() self._registerTraverserFactory(context) environ = self._makeEnviron() def listener(event): raise KeyError self.registry.registerHandler(listener, (INewRequest,)) exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] exception_view = DummyView(exception_response) environ = self._makeEnviron() self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, KeyError) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, exception_response.app_iter) def test_call_pushes_and_pops_threadlocal_manager(self): from pyramid.interfaces import IViewClassifier context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, None, None) router = self._makeOne() start_response = DummyStartResponse() router.threadlocal_manager = DummyThreadLocalManager() router(environ, start_response) self.assertEqual(len(router.threadlocal_manager.pushed), 1) self.assertEqual(len(router.threadlocal_manager.popped), 1) def test_call_route_matches_and_has_factory(self): from pyramid.interfaces import IViewClassifier logger = self._registerLogger() self._registerSettings(debug_routematch=True) self._registerRouteRequest('foo') root = object() def factory(request): return root route = self._connectRoute('foo', 'archives/:action/:article', factory) route.predicates = [DummyPredicate()] context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') self._registerView(view, '', IViewClassifier, None, None) self._registerRootFactory(context) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ['Hello world']) self.assertEqual(start_response.headers, ()) self.assertEqual(start_response.status, '200 OK') request = view.request self.assertEqual(request.view_name, '') self.assertEqual(request.subpath, []) self.assertEqual(request.context, context) self.assertEqual(request.root, root) matchdict = {'action':'action1', 'article':'article1'} self.assertEqual(request.matchdict, matchdict) self.assertEqual(request.matched_route.name, 'foo') self.assertEqual(len(logger.messages), 1) self.assertTrue( logger.messages[0].startswith( "route matched for url http://localhost:8080" "/archives/action1/article1; " "route_name: 'foo', " "path_info: ") ) self.assertTrue( "predicates: 'predicate'" in logger.messages[0] ) def test_call_route_match_miss_debug_routematch(self): from pyramid.httpexceptions import HTTPNotFound logger = self._registerLogger() self._registerSettings(debug_routematch=True) self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article') context = DummyContext() self._registerTraverserFactory(context) environ = self._makeEnviron(PATH_INFO='/wontmatch') self._registerRootFactory(context) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(HTTPNotFound, router, environ, start_response) self.assertEqual(len(logger.messages), 1) self.assertEqual( logger.messages[0], 'no route matched for url http://localhost:8080/wontmatch') def test_call_route_matches_doesnt_overwrite_subscriber_iface(self): from pyramid.interfaces import INewRequest from pyramid.interfaces import IViewClassifier from zope.interface import alsoProvides from zope.interface import Interface self._registerRouteRequest('foo') class IFoo(Interface): pass def listener(event): alsoProvides(event.request, IFoo) self.registry.registerHandler(listener, (INewRequest,)) root = object() def factory(request): return root self._connectRoute('foo', 'archives/:action/:article', factory) context = DummyContext() self._registerTraverserFactory(context) response = DummyResponse() response.app_iter = ['Hello world'] view = DummyView(response) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') self._registerView(view, '', IViewClassifier, None, None) self._registerRootFactory(context) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ['Hello world']) self.assertEqual(start_response.headers, ()) self.assertEqual(start_response.status, '200 OK') request = view.request self.assertEqual(request.view_name, '') self.assertEqual(request.subpath, []) self.assertEqual(request.context, context) self.assertEqual(request.root, root) matchdict = {'action':'action1', 'article':'article1'} self.assertEqual(request.matchdict, matchdict) self.assertEqual(request.matched_route.name, 'foo') self.assertTrue(IFoo.providedBy(request)) def test_root_factory_raises_notfound(self): from pyramid.interfaces import IRootFactory from pyramid.httpexceptions import HTTPNotFound from zope.interface import Interface from zope.interface import directlyProvides def rootfactory(request): raise HTTPNotFound('from root factory') self.registry.registerUtility(rootfactory, IRootFactory) class IContext(Interface): pass context = DummyContext() directlyProvides(context, IContext) environ = self._makeEnviron() router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPNotFound, router, environ, start_response) self.assertTrue('from root factory' in why.args[0]) def test_root_factory_raises_forbidden(self): from pyramid.interfaces import IRootFactory from pyramid.httpexceptions import HTTPForbidden from zope.interface import Interface from zope.interface import directlyProvides def rootfactory(request): raise HTTPForbidden('from root factory') self.registry.registerUtility(rootfactory, IRootFactory) class IContext(Interface): pass context = DummyContext() directlyProvides(context, IContext) environ = self._makeEnviron() router = self._makeOne() start_response = DummyStartResponse() why = exc_raised(HTTPForbidden, router, environ, start_response) self.assertTrue('from root factory' in why.args[0]) def test_root_factory_exception_propagating(self): from pyramid.interfaces import IRootFactory from zope.interface import Interface from zope.interface import directlyProvides def rootfactory(request): raise RuntimeError() self.registry.registerUtility(rootfactory, IRootFactory) class IContext(Interface): pass context = DummyContext() directlyProvides(context, IContext) environ = self._makeEnviron() router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(RuntimeError, router, environ, start_response) def test_traverser_exception_propagating(self): environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context, raise_error=RuntimeError()) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(RuntimeError, router, environ, start_response) def test_call_view_exception_propagating(self): from zope.interface import Interface from zope.interface import directlyProvides class IContext(Interface): pass from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRequestFactory from pyramid.interfaces import IExceptionViewClassifier def rfactory(environ): return request self.registry.registerUtility(rfactory, IRequestFactory) from pyramid.request import Request request = Request.blank('/') context = DummyContext() directlyProvides(context, IContext) self._registerTraverserFactory(context, subpath=['']) response = DummyResponse() response.app_iter = ['OK'] view = DummyView(response, raise_exception=RuntimeError) environ = self._makeEnviron() def exception_view(context, request): self.assertEqual(request.exc_info[0], RuntimeError) return response self._registerView(view, '', IViewClassifier, IRequest, IContext) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, RuntimeError) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ['OK']) # we clean up the exc_info and exception after the request self.assertEqual(request.exception, None) self.assertEqual(request.exc_info, None) def test_call_view_raises_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest response = DummyResponse() exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] view = DummyView(response, raise_exception=RuntimeError) def exception_view(context, request): self.assertEqual(request.exception.__class__, RuntimeError) return exception_response environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, None) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, RuntimeError) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ["Hello, world"]) def test_call_view_raises_super_exception_sub_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest class SuperException(Exception): pass class SubException(SuperException): pass response = DummyResponse() exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] view = DummyView(response, raise_exception=SuperException) exception_view = DummyView(exception_response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, None) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, SubException) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(SuperException, router, environ, start_response) def test_call_view_raises_sub_exception_super_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest class SuperException(Exception): pass class SubException(SuperException): pass response = DummyResponse() exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] view = DummyView(response, raise_exception=SubException) exception_view = DummyView(exception_response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, None) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, SuperException) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ["Hello, world"]) def test_call_view_raises_exception_another_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest class MyException(Exception): pass class AnotherException(Exception): pass response = DummyResponse() exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] view = DummyView(response, raise_exception=MyException) exception_view = DummyView(exception_response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, None) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, AnotherException) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(MyException, router, environ, start_response) def test_root_factory_raises_exception_view(self): from pyramid.interfaces import IRootFactory from pyramid.interfaces import IRequest from pyramid.interfaces import IExceptionViewClassifier def rootfactory(request): raise RuntimeError() self.registry.registerUtility(rootfactory, IRootFactory) exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] exception_view = DummyView(exception_response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, RuntimeError) environ = self._makeEnviron() router = self._makeOne() start_response = DummyStartResponse() app_iter = router(environ, start_response) self.assertEqual(app_iter, ["Hello, world"]) def test_traverser_raises_exception_view(self): from pyramid.interfaces import IRequest from pyramid.interfaces import IExceptionViewClassifier environ = self._makeEnviron() context = DummyContext() self._registerTraverserFactory(context, raise_error=RuntimeError()) exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] exception_view = DummyView(exception_response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, RuntimeError) router = self._makeOne() start_response = DummyStartResponse() result = router(environ, start_response) self.assertEqual(result, ["Hello, world"]) def test_exception_view_returns_non_iresponse(self): from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier environ = self._makeEnviron() response = DummyResponse() view = DummyView(response, raise_exception=RuntimeError) self._registerView(self.config.derive_view(view), '', IViewClassifier, IRequest, None) exception_view = DummyView(None) self._registerView(self.config.derive_view(exception_view), '', IExceptionViewClassifier, IRequest, RuntimeError) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(ValueError, router, environ, start_response) def test_call_route_raises_route_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, req_iface, RuntimeError) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() result = router(environ, start_response) self.assertEqual(result, ["Hello, world"]) def test_call_view_raises_exception_route_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, IRequest, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, req_iface, RuntimeError) environ = self._makeEnviron() start_response = DummyStartResponse() router = self._makeOne() self.assertRaises(RuntimeError, router, environ, start_response) def test_call_route_raises_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, RuntimeError) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() result = router(environ, start_response) self.assertEqual(result, ["Hello, world"]) def test_call_route_raises_super_exception_sub_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest class SuperException(Exception): pass class SubException(SuperException): pass req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=SuperException) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, SubException) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() self.assertRaises(SuperException, router, environ, start_response) def test_call_route_raises_sub_exception_super_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest class SuperException(Exception): pass class SubException(SuperException): pass req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=SubException) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, SuperException) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() result = router(environ, start_response) self.assertEqual(result, ["Hello, world"]) def test_call_route_raises_exception_another_exception_view(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest class MyException(Exception): pass class AnotherException(Exception): pass req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=MyException) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, AnotherException) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() self.assertRaises(MyException, router, environ, start_response) def test_call_route_raises_exception_view_specializing(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest req_iface = self._registerRouteRequest('foo') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, IRequest, RuntimeError) response_spec = DummyResponse() response_spec.app_iter = ["Hello, special world"] exception_view_spec = DummyView(response_spec) self._registerView(exception_view_spec, '', IExceptionViewClassifier, req_iface, RuntimeError) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() result = router(environ, start_response) self.assertEqual(result, ["Hello, special world"]) def test_call_route_raises_exception_view_another_route(self): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier req_iface = self._registerRouteRequest('foo') another_req_iface = self._registerRouteRequest('bar') self._connectRoute('foo', 'archives/:action/:article', None) view = DummyView(DummyResponse(), raise_exception=RuntimeError) self._registerView(view, '', IViewClassifier, req_iface, None) response = DummyResponse() response.app_iter = ["Hello, world"] exception_view = DummyView(response) self._registerView(exception_view, '', IExceptionViewClassifier, another_req_iface, RuntimeError) environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') start_response = DummyStartResponse() router = self._makeOne() self.assertRaises(RuntimeError, router, environ, start_response) def test_call_view_raises_exception_view_route(self): from pyramid.interfaces import IRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IExceptionViewClassifier req_iface = self._registerRouteRequest('foo') response = DummyResponse() exception_response = DummyResponse() exception_response.app_iter = ["Hello, world"] view = DummyView(response, raise_exception=RuntimeError) exception_view = DummyView(exception_response) environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, IRequest, None) self._registerView(exception_view, '', IExceptionViewClassifier, req_iface, RuntimeError) router = self._makeOne() start_response = DummyStartResponse() self.assertRaises(RuntimeError, router, environ, start_response) class DummyPredicate(object): def __call__(self, info, request): return True def text(self): return 'predicate' class DummyContext: pass class DummyView: def __init__(self, response, raise_exception=None): self.response = response self.raise_exception = raise_exception def __call__(self, context, request): self.context = context self.request = request if not self.raise_exception is None: raise self.raise_exception return self.response class DummyRootFactory: def __init__(self, root): self.root = root def __call__(self, environ): return self.root class DummyStartResponse: status = () headers = () def __call__(self, status, headers): self.status = status self.headers = headers from pyramid.interfaces import IResponse from zope.interface import implementer @implementer(IResponse) class DummyResponse(object): headerlist = () app_iter = () environ = None def __init__(self, status='200 OK'): self.status = status def __call__(self, environ, start_response): self.environ = environ start_response(self.status, self.headerlist) return self.app_iter class DummyThreadLocalManager: def __init__(self): self.pushed = [] self.popped = [] def push(self, val): self.pushed.append(val) def pop(self): self.popped.append(True) class DummyAuthenticationPolicy: pass class DummyLogger: def __init__(self): self.messages = [] def info(self, msg): self.messages.append(msg) warn = info debug = info def exc_raised(exc, func, *arg, **kw): try: func(*arg, **kw) except exc as e: return e else: raise AssertionError('%s not raised' % exc) # pragma: no cover pyramid-1.4.5/pyramid/tests/test_scripting.py0000664000175000017500000001657612203712502020715 0ustar takakitakakiimport unittest class Test_get_root(unittest.TestCase): def _callFUT(self, app, request=None): from pyramid.scripting import get_root return get_root(app, request) def _makeRegistry(self): return DummyRegistry([DummyFactory]) def test_it_norequest(self): registry = self._makeRegistry() app = DummyApp(registry=registry) root, closer = self._callFUT(app) self.assertEqual(len(app.threadlocal_manager.pushed), 1) pushed = app.threadlocal_manager.pushed[0] self.assertEqual(pushed['registry'], registry) self.assertEqual(pushed['request'].registry, app.registry) self.assertEqual(len(app.threadlocal_manager.popped), 0) closer() self.assertEqual(len(app.threadlocal_manager.popped), 1) def test_it_withrequest(self): registry = self._makeRegistry() app = DummyApp(registry=registry) request = DummyRequest({}) root, closer = self._callFUT(app, request) self.assertEqual(len(app.threadlocal_manager.pushed), 1) pushed = app.threadlocal_manager.pushed[0] self.assertEqual(pushed['registry'], registry) self.assertEqual(pushed['request'], request) self.assertEqual(len(app.threadlocal_manager.popped), 0) closer() self.assertEqual(len(app.threadlocal_manager.popped), 1) def test_it_requestfactory_overridden(self): registry = self._makeRegistry() app = DummyApp(registry=registry) root, closer = self._callFUT(app) self.assertEqual(len(app.threadlocal_manager.pushed), 1) pushed = app.threadlocal_manager.pushed[0] self.assertEqual(pushed['request'].environ['path'], '/') class Test_prepare(unittest.TestCase): def _callFUT(self, request=None, registry=None): from pyramid.scripting import prepare return prepare(request, registry) def _makeRegistry(self, L=None): if L is None: L = [None, DummyFactory] return DummyRegistry(L) def setUp(self): from pyramid.threadlocal import manager self.manager = manager self.default = manager.get() def test_it_no_valid_apps(self): from pyramid.exceptions import ConfigurationError self.assertRaises(ConfigurationError, self._callFUT) def test_it_norequest(self): registry = self._makeRegistry([DummyFactory, None, DummyFactory]) info = self._callFUT(registry=registry) root, closer, request = info['root'], info['closer'], info['request'] pushed = self.manager.get() self.assertEqual(pushed['registry'], registry) self.assertEqual(pushed['request'].registry, registry) self.assertEqual(root.a, (pushed['request'],)) closer() self.assertEqual(self.default, self.manager.get()) self.assertEqual(request.context, root) def test_it_withrequest_hasregistry(self): request = DummyRequest({}) registry = request.registry = self._makeRegistry() info = self._callFUT(request=request) root, closer, request = info['root'], info['closer'], info['request'] pushed = self.manager.get() self.assertEqual(pushed['request'], request) self.assertEqual(pushed['registry'], registry) self.assertEqual(pushed['request'].registry, registry) self.assertEqual(root.a, (request,)) closer() self.assertEqual(self.default, self.manager.get()) self.assertEqual(request.context, root) self.assertEqual(request.registry, registry) def test_it_withrequest_noregistry(self): request = DummyRequest({}) registry = self._makeRegistry() info = self._callFUT(request=request, registry=registry) root, closer, request = info['root'], info['closer'], info['request'] closer() self.assertEqual(request.context, root) # should be set by prepare self.assertEqual(request.registry, registry) def test_it_with_request_and_registry(self): request = DummyRequest({}) registry = request.registry = self._makeRegistry() info = self._callFUT(request=request, registry=registry) root, closer, root = info['root'], info['closer'], info['root'] pushed = self.manager.get() self.assertEqual(pushed['request'], request) self.assertEqual(pushed['registry'], registry) self.assertEqual(pushed['request'].registry, registry) self.assertEqual(root.a, (request,)) closer() self.assertEqual(self.default, self.manager.get()) self.assertEqual(request.context, root) def test_it_with_request_context_already_set(self): request = DummyRequest({}) context = Dummy() request.context = context registry = request.registry = self._makeRegistry() info = self._callFUT(request=request, registry=registry) root, closer, root = info['root'], info['closer'], info['root'] closer() self.assertEqual(request.context, context) def test_it_with_extensions(self): exts = Dummy() request = DummyRequest({}) registry = request.registry = self._makeRegistry([exts, DummyFactory]) info = self._callFUT(request=request, registry=registry) self.assertEqual(request.extensions, exts) root, closer = info['root'], info['closer'] closer() class Test__make_request(unittest.TestCase): def _callFUT(self, path='/', registry=None): from pyramid.scripting import _make_request return _make_request(path, registry) def _makeRegistry(self): return DummyRegistry([DummyFactory]) def test_it_with_registry(self): registry = self._makeRegistry() request = self._callFUT('/', registry) self.assertEqual(request.environ['path'], '/') self.assertEqual(request.registry, registry) def test_it_with_no_registry(self): from pyramid.config import global_registries registry = self._makeRegistry() global_registries.add(registry) try: request = self._callFUT('/hello') self.assertEqual(request.environ['path'], '/hello') self.assertEqual(request.registry, registry) finally: global_registries.empty() class Dummy: pass dummy_root = Dummy() class DummyFactory(object): @classmethod def blank(cls, path): req = DummyRequest({'path': path}) return req def __init__(self, *a, **kw): self.a = a self.kw = kw class DummyRegistry(object): def __init__(self, utilities): self.utilities = utilities def queryUtility(self, iface, default=None): # pragma: no cover if self.utilities: return self.utilities.pop(0) return default class DummyApp: def __init__(self, registry=None): self.threadlocal_manager = DummyThreadLocalManager() if registry: self.registry = registry def root_factory(self, environ): return dummy_root class DummyThreadLocalManager: def __init__(self): self.pushed = [] self.popped = [] def push(self, item): self.pushed.append(item) def pop(self): self.popped.append(True) class DummyRequest: matchdict = None matched_route = None def __init__(self, environ): self.environ = environ def _set_extensions(self, exts): self.extensions = exts pyramid-1.4.5/pyramid/tests/test_i18n.py0000664000175000017500000004130012210154276017457 0ustar takakitakaki# -*- coding: utf-8 -*- # import os here = os.path.dirname(__file__) localedir = os.path.join(here, 'pkgs', 'localeapp', 'locale') import unittest from pyramid.testing import cleanUp class TestTranslationString(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.i18n import TranslationString return TranslationString(*arg, **kw) def test_it(self): # this is part of the API, we don't actually need to test much more # than that it's importable ts = self._makeOne('a') self.assertEqual(ts, 'a') class TestTranslationStringFactory(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.i18n import TranslationStringFactory return TranslationStringFactory(*arg, **kw) def test_it(self): # this is part of the API, we don't actually need to test much more # than that it's importable factory = self._makeOne('a') self.assertEqual(factory('').domain, 'a') class TestLocalizer(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.i18n import Localizer return Localizer(*arg, **kw) def test_ctor(self): localizer = self._makeOne('en_US', None) self.assertEqual(localizer.locale_name, 'en_US') self.assertEqual(localizer.translations, None) def test_translate(self): translations = DummyTranslations() localizer = self._makeOne(None, translations) self.assertEqual(localizer.translate('123', domain='1', mapping={}), '123') self.assertTrue(localizer.translator) def test_pluralize(self): translations = DummyTranslations() localizer = self._makeOne(None, translations) result = localizer.pluralize('singular', 'plural', 1, domain='1', mapping={}) self.assertEqual(result, 'singular') self.assertTrue(localizer.pluralizer) def test_pluralize_pluralizer_already_added(self): translations = DummyTranslations() localizer = self._makeOne(None, translations) def pluralizer(*arg, **kw): return arg, kw localizer.pluralizer = pluralizer result = localizer.pluralize('singular', 'plural', 1, domain='1', mapping={}) self.assertEqual( result, (('singular', 'plural', 1), {'domain': '1', 'mapping': {}}) ) self.assertTrue(localizer.pluralizer is pluralizer) def test_pluralize_default_translations(self): # test that even without message ids loaded that # "localizer.pluralize" "works" instead of raising an inscrutable # "translations object has no attr 'plural' error; see # see https://github.com/Pylons/pyramid/issues/235 from pyramid.i18n import Translations translations = Translations() translations._catalog = {} localizer = self._makeOne(None, translations) result = localizer.pluralize('singular', 'plural', 2, domain='1', mapping={}) self.assertEqual(result, 'plural') class Test_negotiate_locale_name(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.i18n import negotiate_locale_name return negotiate_locale_name(request) def _registerImpl(self, impl): from pyramid.threadlocal import get_current_registry registry = get_current_registry() from pyramid.interfaces import ILocaleNegotiator registry.registerUtility(impl, ILocaleNegotiator) def test_no_registry_on_request(self): self._registerImpl(dummy_negotiator) request = DummyRequest() result = self._callFUT(request) self.assertEqual(result, 'bogus') def test_with_registry_on_request(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() self._registerImpl(dummy_negotiator) request = DummyRequest() request.registry = registry result = self._callFUT(request) self.assertEqual(result, 'bogus') def test_default_from_settings(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() settings = {'default_locale_name':'settings'} registry.settings = settings request = DummyRequest() request.registry = registry result = self._callFUT(request) self.assertEqual(result, 'settings') def test_use_default_locale_negotiator(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() request = DummyRequest() request.registry = registry request._LOCALE_ = 'locale' result = self._callFUT(request) self.assertEqual(result, 'locale') def test_default_default(self): request = DummyRequest() result = self._callFUT(request) self.assertEqual(result, 'en') class Test_get_locale_name(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.i18n import get_locale_name return get_locale_name(request) def _registerImpl(self, impl): from pyramid.threadlocal import get_current_registry registry = get_current_registry() from pyramid.interfaces import ILocaleNegotiator registry.registerUtility(impl, ILocaleNegotiator) def test_name_on_request(self): request = DummyRequest() request.locale_name = 'ie' result = self._callFUT(request) self.assertEqual(result, 'ie') def test_name_not_on_request(self): self._registerImpl(dummy_negotiator) request = DummyRequest() result = self._callFUT(request) self.assertEqual(result, 'bogus') self.assertEqual(request.locale_name, 'bogus') class Test_make_localizer(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, locale, tdirs): from pyramid.i18n import make_localizer return make_localizer(locale, tdirs) def test_locale_from_mo(self): from pyramid.i18n import Localizer localedirs = [localedir] locale_name = 'de' result = self._callFUT(locale_name, localedirs) self.assertEqual(result.__class__, Localizer) self.assertEqual(result.translate('Approve', 'deformsite'), 'Genehmigen') self.assertEqual(result.translate('Approve'), 'Approve') self.assertTrue(hasattr(result, 'pluralize')) def test_locale_from_mo_bad_mo(self): from pyramid.i18n import Localizer localedirs = [localedir] locale_name = 'be' result = self._callFUT(locale_name, localedirs) self.assertEqual(result.__class__, Localizer) self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') def test_locale_from_mo_mo_isdir(self): from pyramid.i18n import Localizer localedirs = [localedir] locale_name = 'gb' result = self._callFUT(locale_name, localedirs) self.assertEqual(result.__class__, Localizer) self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') def test_territory_fallback(self): from pyramid.i18n import Localizer localedirs = [localedir] locale_name = 'de_DE' result = self._callFUT(locale_name, localedirs) self.assertEqual(result.__class__, Localizer) self.assertEqual(result.translate('Submit', 'deformsite'), 'different') # prefer translations from de_DE locale self.assertEqual(result.translate('Approve', 'deformsite'), 'Genehmigen') # missing from de_DE locale, but in de class Test_get_localizer(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.i18n import get_localizer return get_localizer(request) def test_no_registry_on_request(self): request = DummyRequest() request.localizer = '123' result = self._callFUT(request) self.assertEqual(result, '123') def test_with_registry_on_request(self): from pyramid.threadlocal import get_current_registry registry = get_current_registry() request = DummyRequest() request.localizer = '123' request.registry = registry result = self._callFUT(request) self.assertEqual(result, '123') def test_locale_on_request(self): request = DummyRequest() request.localizer = 'abc' result = self._callFUT(request) self.assertEqual(result, 'abc') def test_locale_from_registry(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ILocalizer registry = get_current_registry() locale = 'abc' registry.registerUtility(locale, ILocalizer, name='en') request = DummyRequest() request.locale_name = 'en' result = self._callFUT(request) self.assertEqual(result, 'abc') def test_locale_from_mo(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ITranslationDirectories from pyramid.i18n import Localizer registry = get_current_registry() localedirs = [localedir] registry.registerUtility(localedirs, ITranslationDirectories) request = DummyRequest() request.locale_name = 'de' result = self._callFUT(request) self.assertEqual(result.__class__, Localizer) self.assertEqual(result.translate('Approve', 'deformsite'), 'Genehmigen') self.assertEqual(result.translate('Approve'), 'Approve') self.assertTrue(hasattr(result, 'pluralize')) def test_locale_from_mo_bad_mo(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ITranslationDirectories from pyramid.i18n import Localizer registry = get_current_registry() localedirs = [localedir] registry.registerUtility(localedirs, ITranslationDirectories) request = DummyRequest() request.locale_name = 'be' result = self._callFUT(request) self.assertEqual(result.__class__, Localizer) self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') class Test_default_locale_negotiator(unittest.TestCase): def setUp(self): cleanUp() def tearDown(self): cleanUp() def _callFUT(self, request): from pyramid.i18n import default_locale_negotiator return default_locale_negotiator(request) def test_from_none(self): request = DummyRequest() result = self._callFUT(request) self.assertEqual(result, None) def test_from_request_attr(self): request = DummyRequest() request._LOCALE_ = 'foo' result = self._callFUT(request) self.assertEqual(result, 'foo') def test_from_params(self): request = DummyRequest() request.params['_LOCALE_'] = 'foo' result = self._callFUT(request) self.assertEqual(result, 'foo') def test_from_cookies(self): request = DummyRequest() request.cookies['_LOCALE_'] = 'foo' result = self._callFUT(request) self.assertEqual(result, 'foo') class TestTranslations(unittest.TestCase): def _getTargetClass(self): from pyramid.i18n import Translations return Translations def _makeOne(self): messages1 = [ ('foo', 'Voh'), (('foo1', 1), 'Voh1'), ] messages2 = [ ('foo', 'VohD'), (('foo1', 1), 'VohD1'), ] klass = self._getTargetClass() translations1 = klass(None, domain='messages') translations1._catalog = dict(messages1) translations1.plural = lambda *arg: 1 translations2 = klass(None, domain='messages1') translations2._catalog = dict(messages2) translations2.plural = lambda *arg: 1 translations = translations1.add(translations2, merge=False) return translations def test_load_locales_None(self): import gettext klass = self._getTargetClass() result = klass.load(localedir, None, domain=None) self.assertEqual(result.__class__, gettext.NullTranslations) def test_load_domain_None(self): import gettext locales = ['de', 'en'] klass = self._getTargetClass() result = klass.load(localedir, locales, domain=None) self.assertEqual(result.__class__, gettext.NullTranslations) def test_load_found_locale_and_domain(self): locales = ['de', 'en'] klass = self._getTargetClass() result = klass.load(localedir, locales, domain='deformsite') self.assertEqual(result.__class__, klass) def test_load_found_locale_and_domain_locale_is_string(self): locales = 'de' klass = self._getTargetClass() result = klass.load(localedir, locales, domain='deformsite') self.assertEqual(result.__class__, klass) def test___repr__(self): inst = self._makeOne() result = repr(inst) self.assertEqual(result, '') def test_merge_not_gnutranslations(self): inst = self._makeOne() self.assertEqual(inst.merge(None), inst) def test_merge_gnutranslations(self): inst = self._makeOne() inst2 = self._makeOne() inst2._catalog['a'] = 'b' inst.merge(inst2) self.assertEqual(inst._catalog['a'], 'b') def test_merge_gnutranslations_not_translations(self): import gettext t = gettext.GNUTranslations() t._catalog = {'a':'b'} inst = self._makeOne() inst.merge(t) self.assertEqual(inst._catalog['a'], 'b') def test_add_different_domain_merge_true_notexisting(self): inst = self._makeOne() inst2 = self._makeOne() inst2.domain = 'domain2' inst.add(inst2) self.assertEqual(inst._domains['domain2'], inst2) def test_add_different_domain_merge_true_existing(self): inst = self._makeOne() inst2 = self._makeOne() inst3 = self._makeOne() inst2.domain = 'domain2' inst2._catalog['a'] = 'b' inst3.domain = 'domain2' inst._domains['domain2'] = inst3 inst.add(inst2) self.assertEqual(inst._domains['domain2'], inst3) self.assertEqual(inst3._catalog['a'], 'b') def test_add_same_domain_merge_true(self): inst = self._makeOne() inst2 = self._makeOne() inst2._catalog['a'] = 'b' inst.add(inst2) self.assertEqual(inst._catalog['a'], 'b') def test_dgettext(self): t = self._makeOne() self.assertEqual(t.dgettext('messages', 'foo'), 'Voh') self.assertEqual(t.dgettext('messages1', 'foo'), 'VohD') def test_ldgettext(self): t = self._makeOne() self.assertEqual(t.ldgettext('messages', 'foo'), b'Voh') self.assertEqual(t.ldgettext('messages1', 'foo'), b'VohD') def test_dugettext(self): t = self._makeOne() self.assertEqual(t.dugettext('messages', 'foo'), 'Voh') self.assertEqual(t.dugettext('messages1', 'foo'), 'VohD') def test_dngettext(self): t = self._makeOne() self.assertEqual(t.dngettext('messages', 'foo1', 'foos1', 1), 'Voh1') self.assertEqual(t.dngettext('messages1', 'foo1', 'foos1', 1), 'VohD1') def test_ldngettext(self): t = self._makeOne() self.assertEqual(t.ldngettext('messages', 'foo1', 'foos1', 1), b'Voh1') self.assertEqual(t.ldngettext('messages1', 'foo1', 'foos1', 1),b'VohD1') def test_dungettext(self): t = self._makeOne() self.assertEqual(t.dungettext('messages', 'foo1', 'foos1', 1), 'Voh1') self.assertEqual(t.dungettext('messages1', 'foo1', 'foos1', 1), 'VohD1') def test_default_germanic_pluralization(self): t = self._getTargetClass()() t._catalog = {} result = t.dungettext('messages', 'foo1', 'foos1', 2) self.assertEqual(result, 'foos1') class DummyRequest(object): def __init__(self): self.params = {} self.cookies = {} def dummy_negotiator(request): return 'bogus' class DummyTranslations(object): def ugettext(self, text): return text gettext = ugettext def ungettext(self, singular, plural, n): return singular ngettext = ungettext pyramid-1.4.5/pyramid/tests/test_threadlocal.py0000664000175000017500000000527311752470445021204 0ustar takakitakakifrom pyramid import testing import unittest class TestThreadLocalManager(unittest.TestCase): def setUp(self): testing.setUp() def tearDown(self): testing.tearDown() def _getTargetClass(self): from pyramid.threadlocal import ThreadLocalManager return ThreadLocalManager def _makeOne(self, default=lambda *x: 1): return self._getTargetClass()(default) def test_init(self): local = self._makeOne() self.assertEqual(local.stack, []) self.assertEqual(local.get(), 1) def test_default(self): def thedefault(): return '123' local = self._makeOne(thedefault) self.assertEqual(local.stack, []) self.assertEqual(local.get(), '123') def test_push_and_pop(self): local = self._makeOne() local.push(True) self.assertEqual(local.get(), True) self.assertEqual(local.pop(), True) self.assertEqual(local.pop(), None) self.assertEqual(local.get(), 1) def test_set_get_and_clear(self): local = self._makeOne() local.set(None) self.assertEqual(local.stack, [None]) self.assertEqual(local.get(), None) local.clear() self.assertEqual(local.get(), 1) local.clear() self.assertEqual(local.get(), 1) class TestGetCurrentRequest(unittest.TestCase): def _callFUT(self): from pyramid.threadlocal import get_current_request return get_current_request() def test_it_None(self): request = self._callFUT() self.assertEqual(request, None) def test_it(self): from pyramid.threadlocal import manager request = object() try: manager.push({'request':request}) self.assertEqual(self._callFUT(), request) finally: manager.pop() self.assertEqual(self._callFUT(), None) class GetCurrentRegistryTests(unittest.TestCase): def setUp(self): testing.setUp() def tearDown(self): testing.tearDown() def _callFUT(self): from pyramid.threadlocal import get_current_registry return get_current_registry() def test_it(self): from pyramid.threadlocal import manager try: manager.push({'registry':123}) self.assertEqual(self._callFUT(), 123) finally: manager.pop() class GetCurrentRegistryWithoutTestingRegistry(unittest.TestCase): def _callFUT(self): from pyramid.threadlocal import get_current_registry return get_current_registry() def test_it(self): from pyramid.registry import global_registry self.assertEqual(self._callFUT(), global_registry) pyramid-1.4.5/pyramid/tests/test_chameleon_zpt.py0000664000175000017500000001453112210154301021523 0ustar takakitakakiimport sys import unittest from pyramid import testing from pyramid.compat import text_type class Base(object): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _getTemplatePath(self, name): import os here = os.path.abspath(os.path.dirname(__file__)) return os.path.join(here, 'fixtures', name) class Test_renderer_factory(Base, unittest.TestCase): def _callFUT(self, info): from pyramid.chameleon_zpt import renderer_factory return renderer_factory(info) def test_it(self): # this test is way too functional from pyramid.chameleon_zpt import ZPTTemplateRenderer info = DummyInfo() result = self._callFUT(info) self.assertEqual(result.__class__, ZPTTemplateRenderer) class ZPTTemplateRendererTests(Base, unittest.TestCase): def _getTargetClass(self): from pyramid.chameleon_zpt import ZPTTemplateRenderer return ZPTTemplateRenderer def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_instance_implements_ITemplate(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ITemplateRenderer path = self._getTemplatePath('minimal.pt') lookup = DummyLookup() verifyObject(ITemplateRenderer, self._makeOne(path, lookup)) def test_class_implements_ITemplate(self): from zope.interface.verify import verifyClass from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) def test_call(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) result = instance({}, {}) self.assertTrue(isinstance(result, text_type)) self.assertEqual(result.rstrip('\n'), '
\n
') def test_template_reified(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template, instance.__dict__['template']) def test_template_with_ichameleon_translate(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.translate, lookup.translate) def test_template_with_debug_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() lookup.debug = True instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.debug, True) def test_template_without_debug_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() lookup.debug = False instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.debug, False) def test_template_with_reload_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() lookup.auto_reload = True instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.auto_reload, True) def test_template_without_reload_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() lookup.auto_reload = False instance = self._makeOne(minimal, lookup) self.assertFalse('template' in instance.__dict__) template = instance.template self.assertEqual(template.auto_reload, False) def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) def test_implementation(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) result = instance.implementation()() self.assertTrue(isinstance(result, text_type)) self.assertEqual(result.rstrip('\n'), '
\n
') def test_macro_supplied(self): minimal = self._getTemplatePath('withmacro.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup, macro='foo') result = instance.implementation()() self.assertEqual(result, '\n Hello!\n') def test_macro_notsupplied(self): minimal = self._getTemplatePath('withmacro.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) result = instance.implementation()() self.assertEqual(result, '\nOutside macro\n\n Hello!\n\n\n\n') def test_macro_template_reload(self): minimal = self._getTemplatePath('withmacro.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup, macro='foo') result = instance.implementation()() self.assertEqual(result, '\n Hello!\n') instance.template.cook( '\nOutside macro\n\n Hello!\n\n\n\n' ) result = instance.implementation()() self.assertEqual(result, '\n Hello!\n') class DummyLookup(object): auto_reload=True debug = True def translate(self, msg): pass class DummyRegistry(object): def queryUtility(self, iface, name): self.queried = iface, name return None def registerUtility(self, impl, iface, name): self.registered = impl, iface, name class DummyInfo(object): def __init__(self): self.registry = DummyRegistry() self.type = '.pt' self.name = 'fixtures/minimal.pt' self.package = sys.modules[__name__] self.settings = {} pyramid-1.4.5/pyramid/tests/test_authentication.py0000664000175000017500000016066712210154276021741 0ustar takakitakakiimport unittest import warnings from pyramid import testing from pyramid.compat import ( text_, bytes_, ) class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase): def setUp(self): from pyramid.interfaces import IDebugLogger self.config = testing.setUp() self.config.registry.registerUtility(self, IDebugLogger) self.messages = [] def tearDown(self): del self.config def debug(self, msg): self.messages.append(msg) def _makeOne(self, userid=None, callback=None): from pyramid.authentication import CallbackAuthenticationPolicy class MyAuthenticationPolicy(CallbackAuthenticationPolicy): def unauthenticated_userid(self, request): return userid policy = MyAuthenticationPolicy() policy.debug = True policy.callback = callback return policy def test_authenticated_userid_no_unauthenticated_userid(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], 'pyramid.tests.test_authentication.MyAuthenticationPolicy.' 'authenticated_userid: call to unauthenticated_userid returned ' 'None; returning None') def test_authenticated_userid_no_callback(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne(userid='fred') self.assertEqual(policy.authenticated_userid(request), 'fred') self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "authenticated_userid: there was no groupfinder callback; " "returning 'fred'") def test_authenticated_userid_with_callback_fail(self): request = DummyRequest(registry=self.config.registry) def callback(userid, request): return None policy = self._makeOne(userid='fred', callback=callback) self.assertEqual(policy.authenticated_userid(request), None) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], 'pyramid.tests.test_authentication.MyAuthenticationPolicy.' 'authenticated_userid: groupfinder callback returned None; ' 'returning None') def test_authenticated_userid_with_callback_success(self): request = DummyRequest(registry=self.config.registry) def callback(userid, request): return [] policy = self._makeOne(userid='fred', callback=callback) self.assertEqual(policy.authenticated_userid(request), 'fred') self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "authenticated_userid: groupfinder callback returned []; " "returning 'fred'") def test_authenticated_userid_fails_cleaning_as_Authenticated(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne(userid='system.Authenticated') self.assertEqual(policy.authenticated_userid(request), None) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "authenticated_userid: use of userid 'system.Authenticated' is " "disallowed by any built-in Pyramid security policy, returning " "None") def test_authenticated_userid_fails_cleaning_as_Everyone(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne(userid='system.Everyone') self.assertEqual(policy.authenticated_userid(request), None) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "authenticated_userid: use of userid 'system.Everyone' is " "disallowed by any built-in Pyramid security policy, returning " "None") def test_effective_principals_no_unauthenticated_userid(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), ['system.Everyone']) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "effective_principals: unauthenticated_userid returned None; " "returning ['system.Everyone']") def test_effective_principals_no_callback(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne(userid='fred') self.assertEqual( policy.effective_principals(request), ['system.Everyone', 'system.Authenticated', 'fred']) self.assertEqual(len(self.messages), 2) self.assertEqual( self.messages[0], 'pyramid.tests.test_authentication.MyAuthenticationPolicy.' 'effective_principals: groupfinder callback is None, so groups ' 'is []') self.assertEqual( self.messages[1], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "effective_principals: returning effective principals: " "['system.Everyone', 'system.Authenticated', 'fred']") def test_effective_principals_with_callback_fail(self): request = DummyRequest(registry=self.config.registry) def callback(userid, request): return None policy = self._makeOne(userid='fred', callback=callback) self.assertEqual( policy.effective_principals(request), ['system.Everyone']) self.assertEqual(len(self.messages), 2) self.assertEqual( self.messages[0], 'pyramid.tests.test_authentication.MyAuthenticationPolicy.' 'effective_principals: groupfinder callback returned None as ' 'groups') self.assertEqual( self.messages[1], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "effective_principals: returning effective principals: " "['system.Everyone']") def test_effective_principals_with_callback_success(self): request = DummyRequest(registry=self.config.registry) def callback(userid, request): return [] policy = self._makeOne(userid='fred', callback=callback) self.assertEqual( policy.effective_principals(request), ['system.Everyone', 'system.Authenticated', 'fred']) self.assertEqual(len(self.messages), 2) self.assertEqual( self.messages[0], 'pyramid.tests.test_authentication.MyAuthenticationPolicy.' 'effective_principals: groupfinder callback returned [] as groups') self.assertEqual( self.messages[1], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "effective_principals: returning effective principals: " "['system.Everyone', 'system.Authenticated', 'fred']") def test_effective_principals_with_unclean_principal_Authenticated(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne(userid='system.Authenticated') self.assertEqual( policy.effective_principals(request), ['system.Everyone']) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "effective_principals: unauthenticated_userid returned disallowed " "'system.Authenticated'; returning ['system.Everyone'] as if it " "was None") def test_effective_principals_with_unclean_principal_Everyone(self): request = DummyRequest(registry=self.config.registry) policy = self._makeOne(userid='system.Everyone') self.assertEqual( policy.effective_principals(request), ['system.Everyone']) self.assertEqual(len(self.messages), 1) self.assertEqual( self.messages[0], "pyramid.tests.test_authentication.MyAuthenticationPolicy." "effective_principals: unauthenticated_userid returned disallowed " "'system.Everyone'; returning ['system.Everyone'] as if it " "was None") class TestRepozeWho1AuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import RepozeWho1AuthenticationPolicy return RepozeWho1AuthenticationPolicy def _makeOne(self, identifier_name='auth_tkt', callback=None): return self._getTargetClass()(identifier_name, callback) def test_class_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IAuthenticationPolicy verifyClass(IAuthenticationPolicy, self._getTargetClass()) def test_instance_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IAuthenticationPolicy verifyObject(IAuthenticationPolicy, self._makeOne()) def test_unauthenticated_userid_returns_None(self): request = DummyRequest({}) policy = self._makeOne() self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_userid(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred'}}) policy = self._makeOne() self.assertEqual(policy.unauthenticated_userid(request), 'fred') def test_authenticated_userid_None(self): request = DummyRequest({}) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred'}}) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), 'fred') def test_authenticated_userid_repoze_who_userid_is_None(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':None}}) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid_with_callback_returns_None(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred'}}) def callback(identity, request): return None policy = self._makeOne(callback=callback) self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid_with_callback_returns_something(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred'}}) def callback(identity, request): return ['agroup'] policy = self._makeOne(callback=callback) self.assertEqual(policy.authenticated_userid(request), 'fred') def test_authenticated_userid_unclean_principal_Authenticated(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'system.Authenticated'}} ) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid_unclean_principal_Everyone(self): request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'system.Everyone'}} ) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) def test_effective_principals_None(self): from pyramid.security import Everyone request = DummyRequest({}) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals_userid_only(self): from pyramid.security import Everyone from pyramid.security import Authenticated request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred'}}) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone, Authenticated, 'fred']) def test_effective_principals_userid_and_groups(self): from pyramid.security import Everyone from pyramid.security import Authenticated request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred', 'groups':['quux', 'biz']}}) def callback(identity, request): return identity['groups'] policy = self._makeOne(callback=callback) self.assertEqual(policy.effective_principals(request), [Everyone, Authenticated, 'fred', 'quux', 'biz']) def test_effective_principals_userid_callback_returns_None(self): from pyramid.security import Everyone request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'fred', 'groups':['quux', 'biz']}}) def callback(identity, request): return None policy = self._makeOne(callback=callback) self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals_repoze_who_userid_is_None(self): from pyramid.security import Everyone request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':None}} ) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals_repoze_who_userid_is_unclean_Everyone(self): from pyramid.security import Everyone request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'system.Everyone'}} ) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals_repoze_who_userid_is_unclean_Authenticated( self): from pyramid.security import Everyone request = DummyRequest( {'repoze.who.identity':{'repoze.who.userid':'system.Authenticated'}} ) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone]) def test_remember_no_plugins(self): request = DummyRequest({}) policy = self._makeOne() result = policy.remember(request, 'fred') self.assertEqual(result, []) def test_remember(self): authtkt = DummyWhoPlugin() request = DummyRequest( {'repoze.who.plugins':{'auth_tkt':authtkt}}) policy = self._makeOne() result = policy.remember(request, 'fred') self.assertEqual(result[0], request.environ) self.assertEqual(result[1], {'repoze.who.userid':'fred'}) def test_forget_no_plugins(self): request = DummyRequest({}) policy = self._makeOne() result = policy.forget(request) self.assertEqual(result, []) def test_forget(self): authtkt = DummyWhoPlugin() request = DummyRequest( {'repoze.who.plugins':{'auth_tkt':authtkt}, 'repoze.who.identity':{'repoze.who.userid':'fred'}, }) policy = self._makeOne() result = policy.forget(request) self.assertEqual(result[0], request.environ) self.assertEqual(result[1], request.environ['repoze.who.identity']) class TestRemoteUserAuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import RemoteUserAuthenticationPolicy return RemoteUserAuthenticationPolicy def _makeOne(self, environ_key='REMOTE_USER', callback=None): return self._getTargetClass()(environ_key, callback) def test_class_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IAuthenticationPolicy verifyClass(IAuthenticationPolicy, self._getTargetClass()) def test_instance_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IAuthenticationPolicy verifyObject(IAuthenticationPolicy, self._makeOne()) def test_unauthenticated_userid_returns_None(self): request = DummyRequest({}) policy = self._makeOne() self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_userid(self): request = DummyRequest({'REMOTE_USER':'fred'}) policy = self._makeOne() self.assertEqual(policy.unauthenticated_userid(request), 'fred') def test_authenticated_userid_None(self): request = DummyRequest({}) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid(self): request = DummyRequest({'REMOTE_USER':'fred'}) policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), 'fred') def test_effective_principals_None(self): from pyramid.security import Everyone request = DummyRequest({}) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals(self): from pyramid.security import Everyone from pyramid.security import Authenticated request = DummyRequest({'REMOTE_USER':'fred'}) policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone, Authenticated, 'fred']) def test_remember(self): request = DummyRequest({'REMOTE_USER':'fred'}) policy = self._makeOne() result = policy.remember(request, 'fred') self.assertEqual(result, []) def test_forget(self): request = DummyRequest({'REMOTE_USER':'fred'}) policy = self._makeOne() result = policy.forget(request) self.assertEqual(result, []) class TestAuthTktAuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import AuthTktAuthenticationPolicy return AuthTktAuthenticationPolicy def _makeOne(self, callback, cookieidentity, **kw): inst = self._getTargetClass()('secret', callback, **kw) inst.cookie = DummyCookieHelper(cookieidentity) return inst def setUp(self): self.warnings = warnings.catch_warnings() self.warnings.__enter__() warnings.simplefilter('ignore', DeprecationWarning) def tearDown(self): self.warnings.__exit__(None, None, None) def test_allargs(self): # pass all known args inst = self._getTargetClass()( 'secret', callback=None, cookie_name=None, secure=False, include_ip=False, timeout=None, reissue_time=None, hashalg='sha512', ) self.assertEqual(inst.callback, None) def test_hashalg_override(self): # important to ensure hashalg is passed to cookie helper inst = self._getTargetClass()('secret', hashalg='sha512') self.assertEqual(inst.cookie.hashalg, 'sha512') def test_unauthenticated_userid_returns_None(self): request = DummyRequest({}) policy = self._makeOne(None, None) self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_userid(self): request = DummyRequest({'REMOTE_USER':'fred'}) policy = self._makeOne(None, {'userid':'fred'}) self.assertEqual(policy.unauthenticated_userid(request), 'fred') def test_authenticated_userid_no_cookie_identity(self): request = DummyRequest({}) policy = self._makeOne(None, None) self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid_callback_returns_None(self): request = DummyRequest({}) def callback(userid, request): return None policy = self._makeOne(callback, {'userid':'fred'}) self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid(self): request = DummyRequest({}) def callback(userid, request): return True policy = self._makeOne(callback, {'userid':'fred'}) self.assertEqual(policy.authenticated_userid(request), 'fred') def test_effective_principals_no_cookie_identity(self): from pyramid.security import Everyone request = DummyRequest({}) policy = self._makeOne(None, None) self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals_callback_returns_None(self): from pyramid.security import Everyone request = DummyRequest({}) def callback(userid, request): return None policy = self._makeOne(callback, {'userid':'fred'}) self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals(self): from pyramid.security import Everyone from pyramid.security import Authenticated request = DummyRequest({}) def callback(userid, request): return ['group.foo'] policy = self._makeOne(callback, {'userid':'fred'}) self.assertEqual(policy.effective_principals(request), [Everyone, Authenticated, 'fred', 'group.foo']) def test_remember(self): request = DummyRequest({}) policy = self._makeOne(None, None) result = policy.remember(request, 'fred') self.assertEqual(result, []) def test_remember_with_extra_kargs(self): request = DummyRequest({}) policy = self._makeOne(None, None) result = policy.remember(request, 'fred', a=1, b=2) self.assertEqual(policy.cookie.kw, {'a':1, 'b':2}) self.assertEqual(result, []) def test_forget(self): request = DummyRequest({}) policy = self._makeOne(None, None) result = policy.forget(request) self.assertEqual(result, []) def test_class_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IAuthenticationPolicy verifyClass(IAuthenticationPolicy, self._getTargetClass()) def test_instance_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IAuthenticationPolicy verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) class TestAuthTktCookieHelper(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import AuthTktCookieHelper return AuthTktCookieHelper def _makeOne(self, *arg, **kw): helper = self._getTargetClass()(*arg, **kw) # laziness after moving auth_tkt classes and funcs into # authentication module auth_tkt = DummyAuthTktModule() helper.auth_tkt = auth_tkt helper.AuthTicket = auth_tkt.AuthTicket helper.parse_ticket = auth_tkt.parse_ticket helper.BadTicket = auth_tkt.BadTicket return helper def _makeRequest(self, cookie=None): environ = {'wsgi.version': (1,0)} environ['REMOTE_ADDR'] = '1.1.1.1' environ['SERVER_NAME'] = 'localhost' return DummyRequest(environ, cookie=cookie) def _cookieValue(self, cookie): return eval(cookie.value) def _parseHeaders(self, headers): return [ self._parseHeader(header) for header in headers ] def _parseHeader(self, header): cookie = self._parseCookie(header[1]) return cookie def _parseCookie(self, cookie): from pyramid.compat import SimpleCookie cookies = SimpleCookie() cookies.load(cookie) return cookies.get('auth_tkt') def test_identify_nocookie(self): helper = self._makeOne('secret') request = self._makeRequest() result = helper.identify(request) self.assertEqual(result, None) def test_identify_cookie_value_is_None(self): helper = self._makeOne('secret') request = self._makeRequest(None) result = helper.identify(request) self.assertEqual(result, None) def test_identify_good_cookie_include_ip(self): helper = self._makeOne('secret', include_ip=True) request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], 'userid') self.assertEqual(result['userdata'], '') self.assertEqual(result['timestamp'], 0) self.assertEqual(helper.auth_tkt.value, 'ticket') self.assertEqual(helper.auth_tkt.remote_addr, '1.1.1.1') self.assertEqual(helper.auth_tkt.secret, 'secret') environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_good_cookie_dont_include_ip(self): helper = self._makeOne('secret', include_ip=False) request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], 'userid') self.assertEqual(result['userdata'], '') self.assertEqual(result['timestamp'], 0) self.assertEqual(helper.auth_tkt.value, 'ticket') self.assertEqual(helper.auth_tkt.remote_addr, '0.0.0.0') self.assertEqual(helper.auth_tkt.secret, 'secret') environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_good_cookie_int_useridtype(self): helper = self._makeOne('secret', include_ip=False) helper.auth_tkt.userid = '1' helper.auth_tkt.user_data = 'userid_type:int' request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], 1) self.assertEqual(result['userdata'], 'userid_type:int') self.assertEqual(result['timestamp'], 0) environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:int') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_nonuseridtype_user_data(self): helper = self._makeOne('secret', include_ip=False) helper.auth_tkt.userid = '1' helper.auth_tkt.user_data = 'bogus:int' request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], '1') self.assertEqual(result['userdata'], 'bogus:int') self.assertEqual(result['timestamp'], 0) environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'bogus:int') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_good_cookie_unknown_useridtype(self): helper = self._makeOne('secret', include_ip=False) helper.auth_tkt.userid = 'abc' helper.auth_tkt.user_data = 'userid_type:unknown' request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], 'abc') self.assertEqual(result['userdata'], 'userid_type:unknown') self.assertEqual(result['timestamp'], 0) environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:unknown') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_good_cookie_b64str_useridtype(self): from base64 import b64encode helper = self._makeOne('secret', include_ip=False) helper.auth_tkt.userid = b64encode(b'encoded').strip() helper.auth_tkt.user_data = 'userid_type:b64str' request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], b'encoded') self.assertEqual(result['userdata'], 'userid_type:b64str') self.assertEqual(result['timestamp'], 0) environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:b64str') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_good_cookie_b64unicode_useridtype(self): from base64 import b64encode helper = self._makeOne('secret', include_ip=False) helper.auth_tkt.userid = b64encode(b'\xc3\xa9ncoded').strip() helper.auth_tkt.user_data = 'userid_type:b64unicode' request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(len(result), 4) self.assertEqual(result['tokens'], ()) self.assertEqual(result['userid'], text_(b'\xc3\xa9ncoded', 'utf-8')) self.assertEqual(result['userdata'], 'userid_type:b64unicode') self.assertEqual(result['timestamp'], 0) environ = request.environ self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:b64unicode') self.assertEqual(environ['AUTH_TYPE'],'cookie') def test_identify_bad_cookie(self): helper = self._makeOne('secret', include_ip=True) helper.auth_tkt.parse_raise = True request = self._makeRequest('ticket') result = helper.identify(request) self.assertEqual(result, None) def test_identify_cookie_timed_out(self): helper = self._makeOne('secret', timeout=1) request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) result = helper.identify(request) self.assertEqual(result, None) def test_identify_cookie_reissue(self): import time helper = self._makeOne('secret', timeout=10, reissue_time=0) now = time.time() helper.auth_tkt.timestamp = now helper.now = now + 1 helper.auth_tkt.tokens = (text_('a'), ) request = self._makeRequest('bogus') result = helper.identify(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 1) response = DummyResponse() request.callbacks[0](request, response) self.assertEqual(len(response.headerlist), 3) self.assertEqual(response.headerlist[0][0], 'Set-Cookie') def test_identify_cookie_reissue_already_reissued_this_request(self): import time helper = self._makeOne('secret', timeout=10, reissue_time=0) now = time.time() helper.auth_tkt.timestamp = now helper.now = now + 1 request = self._makeRequest('bogus') request._authtkt_reissued = True result = helper.identify(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 0) def test_identify_cookie_reissue_notyet(self): import time helper = self._makeOne('secret', timeout=10, reissue_time=10) now = time.time() helper.auth_tkt.timestamp = now helper.now = now + 1 request = self._makeRequest('bogus') result = helper.identify(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 0) def test_identify_cookie_reissue_revoked_by_forget(self): import time helper = self._makeOne('secret', timeout=10, reissue_time=0) now = time.time() helper.auth_tkt.timestamp = now helper.now = now + 1 request = self._makeRequest('bogus') result = helper.identify(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 1) result = helper.forget(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 1) response = DummyResponse() request.callbacks[0](request, response) self.assertEqual(len(response.headerlist), 0) def test_identify_cookie_reissue_revoked_by_remember(self): import time helper = self._makeOne('secret', timeout=10, reissue_time=0) now = time.time() helper.auth_tkt.timestamp = now helper.now = now + 1 request = self._makeRequest('bogus') result = helper.identify(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 1) result = helper.remember(request, 'bob') self.assertTrue(result) self.assertEqual(len(request.callbacks), 1) response = DummyResponse() request.callbacks[0](request, response) self.assertEqual(len(response.headerlist), 0) def test_identify_cookie_reissue_with_tokens_default(self): # see https://github.com/Pylons/pyramid/issues#issue/108 import time helper = self._makeOne('secret', timeout=10, reissue_time=0) auth_tkt = DummyAuthTktModule(tokens=['']) helper.auth_tkt = auth_tkt helper.AuthTicket = auth_tkt.AuthTicket helper.parse_ticket = auth_tkt.parse_ticket helper.BadTicket = auth_tkt.BadTicket now = time.time() helper.auth_tkt.timestamp = now helper.now = now + 1 request = self._makeRequest('bogus') result = helper.identify(request) self.assertTrue(result) self.assertEqual(len(request.callbacks), 1) response = DummyResponse() request.callbacks[0](None, response) self.assertEqual(len(response.headerlist), 3) self.assertEqual(response.headerlist[0][0], 'Set-Cookie') self.assertTrue("'tokens': ()" in response.headerlist[0][1]) def test_remember(self): helper = self._makeOne('secret') request = self._makeRequest() result = helper.remember(request, 'userid') self.assertEqual(len(result), 3) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue(result[0][1].endswith('; Path=/')) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue(result[1][1].endswith('; Path=/; Domain=localhost')) self.assertTrue(result[1][1].startswith('auth_tkt=')) self.assertEqual(result[2][0], 'Set-Cookie') self.assertTrue(result[2][1].endswith('; Path=/; Domain=.localhost')) self.assertTrue(result[2][1].startswith('auth_tkt=')) def test_remember_include_ip(self): helper = self._makeOne('secret', include_ip=True) request = self._makeRequest() result = helper.remember(request, 'other') self.assertEqual(len(result), 3) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue(result[0][1].endswith('; Path=/')) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue(result[1][1].endswith('; Path=/; Domain=localhost')) self.assertTrue(result[1][1].startswith('auth_tkt=')) self.assertEqual(result[2][0], 'Set-Cookie') self.assertTrue(result[2][1].endswith('; Path=/; Domain=.localhost')) self.assertTrue(result[2][1].startswith('auth_tkt=')) def test_remember_path(self): helper = self._makeOne('secret', include_ip=True, path="/cgi-bin/app.cgi/") request = self._makeRequest() result = helper.remember(request, 'other') self.assertEqual(len(result), 3) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue(result[0][1].endswith('; Path=/cgi-bin/app.cgi/')) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue(result[1][1].endswith( '; Path=/cgi-bin/app.cgi/; Domain=localhost')) self.assertTrue(result[1][1].startswith('auth_tkt=')) self.assertEqual(result[2][0], 'Set-Cookie') self.assertTrue(result[2][1].endswith( '; Path=/cgi-bin/app.cgi/; Domain=.localhost')) self.assertTrue(result[2][1].startswith('auth_tkt=')) def test_remember_http_only(self): helper = self._makeOne('secret', include_ip=True, http_only=True) request = self._makeRequest() result = helper.remember(request, 'other') self.assertEqual(len(result), 3) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue(result[0][1].endswith('; HttpOnly')) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue(result[1][1].endswith('; HttpOnly')) self.assertTrue(result[1][1].startswith('auth_tkt=')) self.assertEqual(result[2][0], 'Set-Cookie') self.assertTrue(result[2][1].endswith('; HttpOnly')) self.assertTrue(result[2][1].startswith('auth_tkt=')) def test_remember_secure(self): helper = self._makeOne('secret', include_ip=True, secure=True) request = self._makeRequest() result = helper.remember(request, 'other') self.assertEqual(len(result), 3) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue('; Secure' in result[0][1]) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue('; Secure' in result[1][1]) self.assertTrue(result[1][1].startswith('auth_tkt=')) self.assertEqual(result[2][0], 'Set-Cookie') self.assertTrue('; Secure' in result[2][1]) self.assertTrue(result[2][1].startswith('auth_tkt=')) def test_remember_wild_domain_disabled(self): helper = self._makeOne('secret', wild_domain=False) request = self._makeRequest() result = helper.remember(request, 'other') self.assertEqual(len(result), 2) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue(result[0][1].endswith('; Path=/')) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue(result[1][1].endswith('; Path=/; Domain=localhost')) self.assertTrue(result[1][1].startswith('auth_tkt=')) def test_remember_domain_has_port(self): helper = self._makeOne('secret', wild_domain=False) request = self._makeRequest() request.environ['HTTP_HOST'] = 'example.com:80' result = helper.remember(request, 'other') self.assertEqual(len(result), 2) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue(result[0][1].endswith('; Path=/')) self.assertTrue(result[0][1].startswith('auth_tkt=')) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue(result[1][1].endswith('; Path=/; Domain=example.com')) self.assertTrue(result[1][1].startswith('auth_tkt=')) def test_remember_binary_userid(self): import base64 helper = self._makeOne('secret') request = self._makeRequest() result = helper.remember(request, b'userid') values = self._parseHeaders(result) self.assertEqual(len(result), 3) val = self._cookieValue(values[0]) self.assertEqual(val['userid'], bytes_(base64.b64encode(b'userid').strip())) self.assertEqual(val['user_data'], 'userid_type:b64str') def test_remember_int_userid(self): helper = self._makeOne('secret') request = self._makeRequest() result = helper.remember(request, 1) values = self._parseHeaders(result) self.assertEqual(len(result), 3) val = self._cookieValue(values[0]) self.assertEqual(val['userid'], '1') self.assertEqual(val['user_data'], 'userid_type:int') def test_remember_long_userid(self): from pyramid.compat import long helper = self._makeOne('secret') request = self._makeRequest() result = helper.remember(request, long(1)) values = self._parseHeaders(result) self.assertEqual(len(result), 3) val = self._cookieValue(values[0]) self.assertEqual(val['userid'], '1') self.assertEqual(val['user_data'], 'userid_type:int') def test_remember_unicode_userid(self): import base64 helper = self._makeOne('secret') request = self._makeRequest() userid = text_(b'\xc2\xa9', 'utf-8') result = helper.remember(request, userid) values = self._parseHeaders(result) self.assertEqual(len(result), 3) val = self._cookieValue(values[0]) self.assertEqual(val['userid'], base64.b64encode(userid.encode('utf-8'))) self.assertEqual(val['user_data'], 'userid_type:b64unicode') def test_remember_insane_userid(self): helper = self._makeOne('secret') request = self._makeRequest() userid = object() result = helper.remember(request, userid) values = self._parseHeaders(result) self.assertEqual(len(result), 3) value = values[0] self.assertTrue('userid' in value.value) def test_remember_max_age(self): helper = self._makeOne('secret') request = self._makeRequest() result = helper.remember(request, 'userid', max_age='500') values = self._parseHeaders(result) self.assertEqual(len(result), 3) self.assertEqual(values[0]['max-age'], '500') self.assertTrue(values[0]['expires']) def test_remember_tokens(self): helper = self._makeOne('secret') request = self._makeRequest() result = helper.remember(request, 'other', tokens=('foo', 'bar')) self.assertEqual(len(result), 3) self.assertEqual(result[0][0], 'Set-Cookie') self.assertTrue("'tokens': ('foo', 'bar')" in result[0][1]) self.assertEqual(result[1][0], 'Set-Cookie') self.assertTrue("'tokens': ('foo', 'bar')" in result[1][1]) self.assertEqual(result[2][0], 'Set-Cookie') self.assertTrue("'tokens': ('foo', 'bar')" in result[2][1]) def test_remember_unicode_but_ascii_token(self): helper = self._makeOne('secret') request = self._makeRequest() la = text_(b'foo', 'utf-8') result = helper.remember(request, 'other', tokens=(la,)) # tokens must be str type on both Python 2 and 3 self.assertTrue("'tokens': ('foo',)" in result[0][1]) def test_remember_nonascii_token(self): helper = self._makeOne('secret') request = self._makeRequest() la = text_(b'La Pe\xc3\xb1a', 'utf-8') self.assertRaises(ValueError, helper.remember, request, 'other', tokens=(la,)) def test_remember_invalid_token_format(self): helper = self._makeOne('secret') request = self._makeRequest() self.assertRaises(ValueError, helper.remember, request, 'other', tokens=('foo bar',)) self.assertRaises(ValueError, helper.remember, request, 'other', tokens=('1bar',)) def test_forget(self): helper = self._makeOne('secret') request = self._makeRequest() headers = helper.forget(request) self.assertEqual(len(headers), 3) name, value = headers[0] self.assertEqual(name, 'Set-Cookie') self.assertEqual(value, 'auth_tkt=""; Path=/; Max-Age=0; Expires=Wed, 31-Dec-97 23:59:59 GMT') name, value = headers[1] self.assertEqual(name, 'Set-Cookie') self.assertEqual(value, 'auth_tkt=""; Path=/; Domain=localhost; Max-Age=0; ' 'Expires=Wed, 31-Dec-97 23:59:59 GMT') name, value = headers[2] self.assertEqual(name, 'Set-Cookie') self.assertEqual(value, 'auth_tkt=""; Path=/; Domain=.localhost; Max-Age=0; ' 'Expires=Wed, 31-Dec-97 23:59:59 GMT') class TestAuthTicket(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.authentication import AuthTicket return AuthTicket(*arg, **kw) def test_ctor_with_tokens(self): ticket = self._makeOne('secret', 'userid', 'ip', tokens=('a', 'b')) self.assertEqual(ticket.tokens, 'a,b') def test_ctor_with_time(self): ticket = self._makeOne('secret', 'userid', 'ip', time='time') self.assertEqual(ticket.time, 'time') def test_digest(self): ticket = self._makeOne('secret', 'userid', '0.0.0.0', time=10) result = ticket.digest() self.assertEqual(result, '126fd6224912187ee9ffa80e0b81420c') def test_digest_sha512(self): ticket = self._makeOne('secret', 'userid', '0.0.0.0', time=10, hashalg='sha512') result = ticket.digest() self.assertEqual(result, '74770b2e0d5b1a54c2a466ec567a40f7d7823576aa49'\ '3c65fc3445e9b44097f4a80410319ef8cb256a2e60b9'\ 'c2002e48a9e33a3e8ee4379352c04ef96d2cb278') def test_cookie_value(self): ticket = self._makeOne('secret', 'userid', '0.0.0.0', time=10, tokens=('a', 'b')) result = ticket.cookie_value() self.assertEqual(result, '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!') class TestBadTicket(unittest.TestCase): def _makeOne(self, msg, expected=None): from pyramid.authentication import BadTicket return BadTicket(msg, expected) def test_it(self): exc = self._makeOne('msg', expected=True) self.assertEqual(exc.expected, True) self.assertTrue(isinstance(exc, Exception)) class Test_parse_ticket(unittest.TestCase): def _callFUT(self, secret, ticket, ip, hashalg='md5'): from pyramid.authentication import parse_ticket return parse_ticket(secret, ticket, ip, hashalg) def _assertRaisesBadTicket(self, secret, ticket, ip, hashalg='md5'): from pyramid.authentication import BadTicket self.assertRaises(BadTicket,self._callFUT, secret, ticket, ip, hashalg) def test_bad_timestamp(self): ticket = 'x' * 64 self._assertRaisesBadTicket('secret', ticket, 'ip') def test_bad_userid_or_data(self): ticket = 'x' * 32 + '11111111' + 'x' * 10 self._assertRaisesBadTicket('secret', ticket, 'ip') def test_digest_sig_incorrect(self): ticket = 'x' * 32 + '11111111' + 'a!b!c' self._assertRaisesBadTicket('secret', ticket, '0.0.0.0') def test_correct_with_user_data(self): ticket = '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!' result = self._callFUT('secret', ticket, '0.0.0.0') self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) def test_correct_with_user_data_sha512(self): ticket = '7d947cdef99bad55f8e3382a8bd089bb9dd0547f7925b7d189adc1160cab'\ '0ec0e6888faa41eba641a18522b26f19109f3ffafb769767ba8a26d02aae'\ 'ae56599a0000000auserid!a,b!' result = self._callFUT('secret', ticket, '0.0.0.0', 'sha512') self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) class TestSessionAuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import SessionAuthenticationPolicy return SessionAuthenticationPolicy def _makeOne(self, callback=None, prefix=''): return self._getTargetClass()(prefix=prefix, callback=callback) def test_class_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IAuthenticationPolicy verifyClass(IAuthenticationPolicy, self._getTargetClass()) def test_instance_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IAuthenticationPolicy verifyObject(IAuthenticationPolicy, self._makeOne()) def test_unauthenticated_userid_returns_None(self): request = DummyRequest() policy = self._makeOne() self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_userid(self): request = DummyRequest(session={'userid':'fred'}) policy = self._makeOne() self.assertEqual(policy.unauthenticated_userid(request), 'fred') def test_authenticated_userid_no_cookie_identity(self): request = DummyRequest() policy = self._makeOne() self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid_callback_returns_None(self): request = DummyRequest(session={'userid':'fred'}) def callback(userid, request): return None policy = self._makeOne(callback) self.assertEqual(policy.authenticated_userid(request), None) def test_authenticated_userid(self): request = DummyRequest(session={'userid':'fred'}) def callback(userid, request): return True policy = self._makeOne(callback) self.assertEqual(policy.authenticated_userid(request), 'fred') def test_effective_principals_no_identity(self): from pyramid.security import Everyone request = DummyRequest() policy = self._makeOne() self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals_callback_returns_None(self): from pyramid.security import Everyone request = DummyRequest(session={'userid':'fred'}) def callback(userid, request): return None policy = self._makeOne(callback) self.assertEqual(policy.effective_principals(request), [Everyone]) def test_effective_principals(self): from pyramid.security import Everyone from pyramid.security import Authenticated request = DummyRequest(session={'userid':'fred'}) def callback(userid, request): return ['group.foo'] policy = self._makeOne(callback) self.assertEqual(policy.effective_principals(request), [Everyone, Authenticated, 'fred', 'group.foo']) def test_remember(self): request = DummyRequest() policy = self._makeOne() result = policy.remember(request, 'fred') self.assertEqual(request.session.get('userid'), 'fred') self.assertEqual(result, []) def test_forget(self): request = DummyRequest(session={'userid':'fred'}) policy = self._makeOne() result = policy.forget(request) self.assertEqual(request.session.get('userid'), None) self.assertEqual(result, []) def test_forget_no_identity(self): request = DummyRequest() policy = self._makeOne() result = policy.forget(request) self.assertEqual(request.session.get('userid'), None) self.assertEqual(result, []) class TestBasicAuthAuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import BasicAuthAuthenticationPolicy as cls return cls def _makeOne(self, check): return self._getTargetClass()(check, realm='SomeRealm') def test_class_implements_IAuthenticationPolicy(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IAuthenticationPolicy verifyClass(IAuthenticationPolicy, self._getTargetClass()) def test_unauthenticated_userid(self): import base64 request = testing.DummyRequest() request.headers['Authorization'] = 'Basic %s' % base64.b64encode( bytes_('chrisr:password')).decode('ascii') policy = self._makeOne(None) self.assertEqual(policy.unauthenticated_userid(request), 'chrisr') def test_unauthenticated_userid_no_credentials(self): request = testing.DummyRequest() policy = self._makeOne(None) self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_bad_header(self): request = testing.DummyRequest() request.headers['Authorization'] = '...' policy = self._makeOne(None) self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_userid_not_basic(self): request = testing.DummyRequest() request.headers['Authorization'] = 'Complicated things' policy = self._makeOne(None) self.assertEqual(policy.unauthenticated_userid(request), None) def test_unauthenticated_userid_corrupt_base64(self): request = testing.DummyRequest() request.headers['Authorization'] = 'Basic chrisr:password' policy = self._makeOne(None) self.assertEqual(policy.unauthenticated_userid(request), None) def test_authenticated_userid(self): import base64 request = testing.DummyRequest() request.headers['Authorization'] = 'Basic %s' % base64.b64encode( bytes_('chrisr:password')).decode('ascii') def check(username, password, request): return [] policy = self._makeOne(check) self.assertEqual(policy.authenticated_userid(request), 'chrisr') def test_unauthenticated_userid_invalid_payload(self): import base64 request = testing.DummyRequest() request.headers['Authorization'] = 'Basic %s' % base64.b64encode( bytes_('chrisrpassword')).decode('ascii') policy = self._makeOne(None) self.assertEqual(policy.unauthenticated_userid(request), None) def test_remember(self): policy = self._makeOne(None) self.assertEqual(policy.remember(None, None), []) def test_forget(self): policy = self._makeOne(None) self.assertEqual(policy.forget(None), [ ('WWW-Authenticate', 'Basic realm="SomeRealm"')]) class DummyContext: pass class DummyCookies(object): def __init__(self, cookie): self.cookie = cookie def get(self, name): return self.cookie class DummyRequest: def __init__(self, environ=None, session=None, registry=None, cookie=None): self.environ = environ or {} self.session = session or {} self.registry = registry self.callbacks = [] self.cookies = DummyCookies(cookie) def add_response_callback(self, callback): self.callbacks.append(callback) class DummyWhoPlugin: def remember(self, environ, identity): return environ, identity def forget(self, environ, identity): return environ, identity class DummyCookieHelper: def __init__(self, result): self.result = result def identify(self, *arg, **kw): return self.result def remember(self, *arg, **kw): self.kw = kw return [] def forget(self, *arg): return [] class DummyAuthTktModule(object): def __init__(self, timestamp=0, userid='userid', tokens=(), user_data='', parse_raise=False, hashalg="md5"): self.timestamp = timestamp self.userid = userid self.tokens = tokens self.user_data = user_data self.parse_raise = parse_raise self.hashalg = hashalg def parse_ticket(secret, value, remote_addr, hashalg): self.secret = secret self.value = value self.remote_addr = remote_addr if self.parse_raise: raise self.BadTicket() return self.timestamp, self.userid, self.tokens, self.user_data self.parse_ticket = parse_ticket class AuthTicket(object): def __init__(self, secret, userid, remote_addr, **kw): self.secret = secret self.userid = userid self.remote_addr = remote_addr self.kw = kw def cookie_value(self): result = {'secret':self.secret, 'userid':self.userid, 'remote_addr':self.remote_addr} result.update(self.kw) result = repr(result) return result self.AuthTicket = AuthTicket class BadTicket(Exception): pass class DummyResponse: def __init__(self): self.headerlist = [] pyramid-1.4.5/pyramid/tests/test_exceptions.py0000664000175000017500000000567012203712502021065 0ustar takakitakakiimport unittest class TestBWCompat(unittest.TestCase): def test_bwcompat_notfound(self): from pyramid.exceptions import NotFound as one from pyramid.httpexceptions import HTTPNotFound as two self.assertTrue(one is two) def test_bwcompat_forbidden(self): from pyramid.exceptions import Forbidden as one from pyramid.httpexceptions import HTTPForbidden as two self.assertTrue(one is two) class TestNotFound(unittest.TestCase): def _makeOne(self, message): from pyramid.exceptions import NotFound return NotFound(message) def test_it(self): from pyramid.interfaces import IExceptionResponse e = self._makeOne('notfound') self.assertTrue(IExceptionResponse.providedBy(e)) self.assertEqual(e.status, '404 Not Found') self.assertEqual(e.message, 'notfound') def test_response_equivalence(self): from pyramid.exceptions import NotFound from pyramid.httpexceptions import HTTPNotFound self.assertTrue(NotFound is HTTPNotFound) class TestForbidden(unittest.TestCase): def _makeOne(self, message): from pyramid.exceptions import Forbidden return Forbidden(message) def test_it(self): from pyramid.interfaces import IExceptionResponse e = self._makeOne('forbidden') self.assertTrue(IExceptionResponse.providedBy(e)) self.assertEqual(e.status, '403 Forbidden') self.assertEqual(e.message, 'forbidden') def test_response_equivalence(self): from pyramid.exceptions import Forbidden from pyramid.httpexceptions import HTTPForbidden self.assertTrue(Forbidden is HTTPForbidden) class TestConfigurationConflictError(unittest.TestCase): def _makeOne(self, conflicts): from pyramid.exceptions import ConfigurationConflictError return ConfigurationConflictError(conflicts) def test_str(self): conflicts = {'a':('1', '2', '3'), 'b':('4', '5', '6')} exc = self._makeOne(conflicts) self.assertEqual(str(exc), """\ Conflicting configuration actions For: a 1 2 3 For: b 4 5 6""") class TestConfigurationExecutionError(unittest.TestCase): def _makeOne(self, etype, evalue, info): from pyramid.exceptions import ConfigurationExecutionError return ConfigurationExecutionError(etype, evalue, info) def test_str(self): exc = self._makeOne('etype', 'evalue', 'info') self.assertEqual(str(exc), 'etype: evalue\n in:\n info') class TestCyclicDependencyError(unittest.TestCase): def _makeOne(self, cycles): from pyramid.exceptions import CyclicDependencyError return CyclicDependencyError(cycles) def test___str__(self): exc = self._makeOne({'a':['c', 'd'], 'c':['a']}) result = str(exc) self.assertTrue("'a' sorts before ['c', 'd']" in result) self.assertTrue("'c' sorts before ['a']" in result) pyramid-1.4.5/pyramid/tests/test_scaffolds/0000775000175000017500000000000012210157153020271 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/test_init.py0000664000175000017500000000152112203712502022641 0ustar takakitakakiimport unittest class TestPyramidTemplate(unittest.TestCase): def _makeOne(self): from pyramid.scaffolds import PyramidTemplate return PyramidTemplate('name') def test_pre(self): inst = self._makeOne() vars = {'package':'one'} inst.pre('command', 'output dir', vars) self.assertTrue(vars['random_string']) self.assertEqual(vars['package_logger'], 'one') def test_pre_site(self): inst = self._makeOne() vars = {'package':'site'} self.assertRaises(ValueError, inst.pre, 'command', 'output dir', vars) def test_pre_root(self): inst = self._makeOne() vars = {'package':'root'} inst.pre('command', 'output dir', vars) self.assertTrue(vars['random_string']) self.assertEqual(vars['package_logger'], 'app') pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/0000775000175000017500000000000012210157153023620 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/CHANGES.txt_tmpl0000664000175000017500000000003412203712502026457 0ustar takakitakaki0.0 --- - Initial version pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/setup.py_tmpl0000664000175000017500000000172612210154276026377 0ustar takakitakakiimport os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, 'README.txt')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() requires = ['pyramid', 'pyramid_debugtoolbar'] setup(name='{{project}}', version='0.0', description='{{project}}', long_description=README + '\n\n' + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pylons", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author='', author_email='', url='', keywords='web pyramid pylons', packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requires, tests_require=requires, test_suite="{{package}}", entry_points = """\ [paste.app_factory] main = {{package}}:main """, ) pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/README.txt_tmpl0000664000175000017500000000002312203712502026342 0ustar takakitakaki{{project}} README pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/MANIFEST.in_tmpl0000664000175000017500000000020612203712502026405 0ustar takakitakakiinclude *.txt *.ini *.cfg *.rst recursive-include {{package}} *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl0000664000175000017500000000141212203712502027536 0ustar takakitakaki[app:main] use = egg:{{project}} pyramid.reload_templates = false pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.debug_templates = false pyramid.default_locale_name = en [server:main] use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 # Begin logging configuration [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console [logger_{{package_logger}}] level = WARN handlers = qualname = {{package}} [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s # End logging configuration pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/setup.cfg_tmpl0000664000175000017500000000104712203712502026474 0ustar takakitakaki[nosetests] match = ^test nocapture = 1 cover-package = {{package}} with-coverage = 1 cover-erase = 1 [compile_catalog] directory = {{package}}/locale domain = {{project}} statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = {{package}}/locale/{{project}}.pot width = 80 [init_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale [update_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale previous = true pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/0000775000175000017500000000000012210157153025341 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/__init__.py_tmpl0000664000175000017500000000102712203712502030503 0ustar takakitakakifrom pyramid.config import Configurator from {{package}}.resources import Root def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(root_factory=Root, settings=settings) config.add_view('{{package}}.views.my_view', context='{{package}}:resources.Root', renderer='{{package}}:templates/mytemplate.pt') config.add_static_view('static', '{{package}}:static', cache_max_age=3600) return config.make_wsgi_app() pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/views.py_tmpl0000664000175000017500000000007312203712502030101 0ustar takakitakakidef my_view(request): return {'project':'{{project}}'} pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/tests.py_tmpl0000664000175000017500000000061612203712502030111 0ustar takakitakakiimport unittest from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_my_view(self): from {{package}}.views import my_view request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['project'], '{{project}}') pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/0000775000175000017500000000000012210157153026630 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/headerbg.png0000664000175000017500000000031312203712502031071 0ustar takakitakaki‰PNG  IHDR4b·Ît’IDAT8í“AÄ C“\«—›SÏn`b‹íºà)4ÁoŸàû9DRp‘hµÈ¯«×uìô½?÷Þ²‡ ”@»÷¬\SÍ=ýÌ®Š3}½÷­¾ÚOÜÕÜåo¼±FŸ5´9,×cå©ïß»'çãmZó&kÌéä… ´q~øx²Xò5†èßE3˜,óçû÷”01]îsÖIEND®B`‚pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/pylons.css0000664000175000017500000001045212203712502030665 0ustar takakitakakihtml,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */ vertical-align:baseline;background:transparent;} body{line-height:1;} ol,ul{list-style:none;} blockquote,q{quotes:none;} blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;} :focus{outline:0;} ins{text-decoration:none;} del{text-decoration:line-through;} table{border-collapse:collapse;border-spacing:0;} sub{vertical-align:sub;font-size:smaller;line-height:normal;} sup{vertical-align:super;font-size:smaller;line-height:normal;} ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;} ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;} li{display:list-item;} ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;} ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;} ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;} .hidden{display:none;} p{line-height:1.5em;} h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;} h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;} h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;} h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;} html,body{width:100%;height:100%;} body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;} a{color:#1b61d6;text-decoration:none;} a:hover{color:#e88f00;text-decoration:underline;} body h1, body h2, body h3, body h4, body h5, body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;} #wrap{min-height:100%;} #header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;} #header{background:#000000;top:0;font-size:14px;} #footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;} .header,.footer{width:750px;margin-right:auto;margin-left:auto;} .wrapper{width:100%} #top,#top-small,#bottom{width:100%;} #top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} #top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} #bottom{color:#222;background-color:#ffffff;} .top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;} .top{padding-top:40px;} .top-small{padding-top:10px;} #middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;} .app-welcome{margin-top:25px;} .app-name{color:#000000;font-weight:bold;} .bottom{padding-top:50px;} #left{width:350px;float:left;padding-right:25px;} #right{width:350px;float:right;padding-left:25px;} .align-left{text-align:left;} .align-right{text-align:right;} .align-center{text-align:center;} ul.links{margin:0;padding:0;} ul.links li{list-style-type:none;font-size:14px;} form{border-style:none;} fieldset{border-style:none;} input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;} input[type=text],input[type=password]{width:205px;} input[type=submit]{background-color:#ddd;font-weight:bold;} /*Opera Fix*/ body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;} pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/pyramid.png0000664000175000017500000010043712203712502031005 0ustar takakitakaki‰PNG  IHDRî©t߸7 IDATxœì½wœÅ÷ÿ®ê ›µ «œ³–@QÂ`‚cls€qÀÙÆáî±§ó%ßó»óÝÙ>?ççüpNgŸ³}‡98°M8L2QH("i…ÊÚ4©»~|»gzf'íjvV»ª·^­éé®®ªîéíþÔ·¾õ-…eÄsÍ5×Ä»ºô´X“·H}®vÔ03ŒaÐ8Üõ;Õ1¥Q ”:f0¯*Ôv0ëL&ù|&“ÙýðÃî:Z,‹Åb±¨á®€ep\sÍ5ñLÆ™éjo¥6ꔺ˜¦E)eדÀƒR*‰1{ æicxÈdÜ?x^r‹ñ‹Åb±X† +ðFW]uÕ—èÕà]‹Qg+­æ*¥0Æ`Œîê*”R(¥0€qÝÃF™Ê¨ß×üìw¿»oãp×Ïb±X,Ëé…î#„s®»®i\Æ»ÁxÜœ¥§Àó¼a®Ùéˆxç¹cÌK(õË„›üÖc¿ýmçp×Íb±X,Ëéî§8«W¯ŽhÝx)Žú¸Vêu(ñY·Öõá!ðB2Æx 6»˜¯¶6œøþÝw?Ö5ÌU³X,‹Å2Êq†»–Ò¬^½z‚ŽÄ?­õJ©å@t¸ëdÉ¢”R ®N¥âsæÍ]°ãå—·íîJY,‹Åb½X‹û)ÊåW]{™ç¹Ÿ×J_ ÖÂ~*£”Æo†/Ä£|ã¾ûîKw,‹Åb±Œ>¬ÅýdõåW¾¯i­—yžgEû)Ž1­u›1fUÊõÚçÌšñøÎ;SÃ]/‹Åb±X,£ +ÜO-Ôe—_y‡VúËÚÑSìÀÓ‘ƒB2¦Ñ磜ö)“ç?ùÊ+Ûû†»^‹Åb±XFV¸ŸB\ºúÊÛ´V_QJµ+ÚGÆF)¥Î‹D¼‰³gÍ|dçΉᮖÅb±X,–Ñî§«/¿ò=Zó”cŒí#ƒAiçlƒš6cÚ’‡;;·Zñn±X,‹å¤±Âý`Õå—¿ÞxêkJéñÖŸ}´`PJŸ‰“Ò»vìøÝpׯb±X,ËÈÇ ÷aæÂÕWž©1wi­çZÑ>ºP œ5{Îìõ»vìØ2Üõ±X,‹Å2²±Â}¹æškân&óeí8WXÑ>:QJÇ ,;{ÖC;wî<<Üõ±X,‹Å2r±Â}™2}æÛµŠ|Òã` ì2Êc ZéIž¡cL[ë}Hc±X,‹Å2¬p&.½ôÊÀÿUZMµÖöÑü¾j~C,þRgç·»>‹Åb±XF&V¸jÆœ9wjÍ[l™Ó¥TÔY3§ÿº³³³w¸ëc±X,‹eäa…û0pÁ«kG}k­í§:Pú@ç®k‡».‹Åb±XFz¸+p:â8úv0sŒgýÚO—Åx­u#F½ó /œˆÅb±X,ˉ wN7Î;ïÒ9F¹×+ ÖÚ~:áyƘ3ŽÜ3Üõ±X,‹Å2²°÷:ãÄôÐs‡Ûl—ú/¾Õ=¦áº .¸ ‹Åb±X,–`-îuäœsΉ‚¹\iÝàyvPêéˆg<<¸(v¦/w},‹Åb±Œ¬p¯#±ÖÖY&mÎ6žÁJ=M1 `q$9+Ü-‹Åb± ë*SO2™ù`¦ZßöÑRªä‚Œw.òÍb±X,‹¥*¬Å½~hãéåJ«˜±n2#¥ªÐÚUõ¦˜×N9çœÆ}O?mcº[,‹Åb© +ÜëÄ9çœÓf±RϺɜҔç5ùåŒÁÀ¢¹ öAg-²´X,‹Å2ú±Â½N8ŽÓh0óŒ„îêŒZª²ˆW`0¿Ï@ÊõD¸Mg>V¸[,‹¥z•Ý,ƒxf–QˆîuÂÇc‘ ãÍiìÕìyiâÑÚ ­¨…P¯w™J)0F5ª’Åb±XF?qàÓÀ" Sd¿ú€ï¿`Þ¯ó¸þ§D€µÀ[€v`2I˜ Ü4Ow°\˱½N脊ÇmÃèÓÒâîy†hD3kj;^éAk(¥"Œ{-OF|ŸìïgŒÁ£=ÏL:©Œ,‹År:Öç•IÓˆö ÷‹€w#Öüqˆ`?‚ˆó€ÃÀç€Và àI`°øß~ßÁ ÷!ÇF•©™hFL#Æ ûD@ñôö¦¸ìâ¥üí'°pZо¤óùûUè_­ËVEþ ×u•­•blÿ;Åb±X,–¢ Q!MH "ïnÞ ¼„ö?ó·ýÐÊw)0iHœÊ#9ˆr-ÄZÜëD4ÑFeN·PJ)zúR,œ7‘¿ï ¦6üžO¿µ—5Fw/4DG¼ê¯ËÀ­åµ½æƒ±Öçc£”ñâ5«Ô©ƒÆ„–fÿ³Õ_oð—ÒíÆCü½ÀqàUàpØOñ®a‹ÅbLC\;3ºÇUzUã_Œ]þŽ"ÏãuÀ3þþK{ÛnÄ"¾¿~>2'ÉäA–k V¸×cÔh’íÕØD2Ø–>üîULÔBú•$ËôqûU­|õšT"N}ÜcŠQùÂ2|W £âv˜\̦ûßÇcýÏ&äeñ?µ¿”z¹xþâ"Ö>D´ï6 /’uˆ˜ŒUÉb±Œ¦ â5À,ä9sqù69Ñi©ž8ò¬Vô7®8À&äº_ <,~…üV¸×+ÜëÈHWiýh…ôžgȸ.·ß²’+VÍ'ÓÛƒç¼T”[¯H²}_#¿|TÑÚ8tu†…w¬FÈô˜pzE¾÷Ì(àMH7jóäÝŒ4¦+€‹ü^à!àgÈ‹ùÕ!(Ûb±œÚLA|ª¯,Ø>X\¼ Ti© bHy¸xòZ[ ¼~ëuZa}ÜëÉ)àk^/¿p¥}‰4¯¿d!7ßxn2çfC&1;nH°t–!xÅL)^ç¢õ¦túRªºTÞÅòLÉÅ–¼óÄü¥^Äëþ»+Ï¿!7;Ð×b9}ˆwÒ_´‡™ü-"ä-µÁAzAŸ&ïvÛ±Ööºa…{ÝH`ð0fx–*áÁ¥ ½})Î:c Ÿúèe´D!“L ÞH&`zGš?gŠŽvH¥ â9RMþô_ ?]±4¡´yé•Ô¹ßB°0 z•B)åï—ë‹7*\¶‡sà8Ò=ûMà׈EÞb±Œ~Î>TEºËç‚•µA#Ñfžzñ¾qOr†±^§V¸2Dö_{ì`èKdhiˆòÇﻘÉ›I§’(<0AhXJ“LDX>?Í7¸¸.d2¾¨.q…u*›.ï¼*,’ªÌR¹NZëŠi¤î³Œ¥†Dù¿´ ou,Ës)2Ƚ X‰u ¹^1òÅxÓ} ° 2öÈøûFcÀ…S{3ב¡ˆ(Óßï¼ú2NæØRxžÁó<Þú¦³8ï¬idzXîÏ€Ñ~yIi®½(Åã¹ï)C¤Qöôk2iDÖ7—$_‡S©ý¼07É£xcÅøÿ—lÌTÓÆñ{‚ÿ­|¯9mHø²iÀg_L‹Å2ú˜<€´“A™¢ºŒ6’À/w˜½¡í¯_žÿ,þ ñÿ<[ךž¦Xá^O©ÔÊZ¾KäY•µ|€õ©”§RоdŠË/šÇm´/Õ‡çy!˜àx%"±²Çã†;Þ˜b×[_æF¨8¦H]_w¥Šïƒ*¹¿|™¥÷—>°_(È~ô£ƒLæqèBb§—ip%ÄŠÖŠ„w›ÈÀ{p»Ÿ×Hw®Åb]ô m6”ì@è¾XdûVà#¡ï_-Øÿ§œnoµa ÷:‘œ"j´ª(&¡ãªu_HÈÄjy%´Vôö¦9cÁD>ó‘ פI%RAe±îúî2ûûV$ó¦»üÙÛ2|ükQNôb‘œx/W~©}•Ï©øõ)%èË)úbe•®—|c†Õ9¼Îüyf/ò &òH“ÿBÕH÷lb=Ÿœt‹Ÿ‡ˆùjPÈ ©ÈL6l¤Å2ºH¤˜g±Õ+Úë„îu¤˜»EwJ¡OÂÆä—WÓßU$X/Öû›+¥H&3Œçãï?—É“šHu'AûÖåÀÊL•jÊ„\ç”G²OqöB¾þþÇÏí¦ýz;„ZB$|7áíácŒÉ}/8çR×£’0?ÃÂîjðŒw: —Úü÷Ò ÷Ò=» ívÕGù2ˆê‡(Ûb±œúüx ¸¨BºÀ½XQiEØÁ©õ¤H0’“ ¹X.Ÿbyi¥Kî×JݯPÙ}á%||&í’q=>𶜿b ™Þ¤¸Çxžø·>ãùϼ¡£"â=M&i¸ñ’7^ ½ q§ —_x~ŽÒhÊH“· 0Uáº䥵ӯ Gk­ÑJe— 7­šÐä-¹¡pP8h%Ÿ*(³ðÚ>&÷“1àbaûðFànªëúŽŸB&f±X,£‡ƒÀ_P~‡n$äsu©‘ÅR'¬p&*Em f׬tü@"­”KS*oŒÉ-Åê $’.—ž;ƒ¯šI¥ð<L ÿè/›Œ ¬ðâæn”ñH{Š˜V¼÷ê ¦+™ü(1ýb½” SM„—ÜR,ÒLÁ¿øVJçÊ G“ñ×µ–%»ˆ¿eÐàIà½Àÿ¡ºÁfKK½Åb]<|qÅ;‚¸Ã$£ÀSHœ÷ïr:™H,§ÖU¦^$À89÷‹b®aÂn(åŒÿwv¿$ª~ß—H³lñD>sÇy4Ç • ´TN çLû®o€Ñ„Ý̕ր"•2Lé0Üy³Ç_~'ÂñC,šßn´ämÔºð<2ø´ûUä×/ûl)EvlRJgËö<‰°cŒ¡„›½¥2Kº |œòÏ1î¿B–Y,–ÑÃOwºyHÏš:]À¡a¬—Å2dXá^WLö3'ªË„¤|”“ʾÝʰè Êêó.Ü–L¹Œimàß³œéÓ›Hu§D˜~íá:d¨Ê&±Z‡2 ŽQN:\p¦Ë{Ö8|é?žHÞÔÙQ«ë\í¹ôObòEy òV(PA#!eÇ2\$fû<àÍÒ.®Ã w‹e4r ]øôpWÄb©ÖU¦Žä{äœ> ·«"ûú;Š”ö‡Ïù™ûSéþÇÙ–u)²=ŒÄk‡÷Ü|ç/ŸHª' x~eƒŠø“.eÝf‚™XµÔËàûËhäVÔ`4Æ7¥¸ñ¢4×®„¾¤øŽcü¥œÜ1h±Â‡|] ýñå¼Âî1¾‡zÖ¯]ƒ’|%oGòÒ¹¼ƒ‰–ÂKá€B—Yü<ý¼‚ßÔößž4ÝÀ(ïç bu_Mu¶X,‹ÅrÊb-îu$ì“s…éŸN…þï·ï¤¬Ì…å”4·—È+U%™ryÛ ¹åºyd Œçf9*M†`=ˆ#ªÕ®B1rüI™|ÿ2ˆD ¹Áãð±k7šÊœÅël ¾÷?§ð)«Ð9—¬?ð—ÉsÛQá¯è¢å„\¤‚ØBÆP*$¥e@üø7Äu¦KéÀ¶!¯‘Åb±X,C„îu#A0{°…ó‚æa(çÂRé»êç…HGUê 7ŽPB¥(¬b v»{3,š;–w¼e±ˆ!ðÐYŸö°XŸQÐuÍŒ\‡)¨“lO»0¾ÍðÁë]¶îuèê…x,—.ïS0& ÿ#”6×,!ÚÚOt wò¾ãl ûßk•/äà ­<<¿C^ Àr2ü'ðAd*îRt3±ÂÝb±X,#+܇œu·: zU~Ô…_Šì¯lu®à÷­©´Ç¸1 ÜñŽ%tŒ“î ÏkQ*ŽúW™¬…BŠØÏE!iP$“pÆxç•ÿï×"’#¡»¶”¥½ÔIî/tv ê×#Q&¯jz8Ä•¦_2Ëɳx¸¬LšÄþÁºÔÈb±X,–!À ÷:ˆZcrQPªî%¾©B³zx›*bËWR4äÆÑ/¿Âº¨À_ñ®7/àÒ×ú~íO{6«‚A©aK|Ð@ØÚNÖZžmÌ„º<™¤âÆ‹]vîpÏZˆFB5ÍZØs¶óP»¨È9…NÈÿPy×4\­°;S~š~ü‚4á²åtZñîKm8Š <-'ÜA\edÀ…Åb±X,#+ÜëDˆdµ¬ hŠ˜ÊýµBx“b2?ýõhQ”Î;¯ΛÂF„¡;áò–+gqËu3É$Ò¾?{vDlá®(ˆÿîùb8‚2:\P¿:øUE¡H»šxÔðÁë ûFxv47–䯄s,s] …{‘ߣŸp/Ù3Q覓ÿ]+…1^nÀ±µ¾×Š$þ­íXá^kÚ€9H(¾qH‹¼™õöÅ–Z)Àä·l%7à8 Gâyö#wZ€ÀT`¼ÿ]=ȹîvSݼe âb6ù}›ýí=È5î^RCPöh@#×mòû‘er¿îö’›1Úb)‹îuED¬vræðBw‹@ær”ý¡õ57œO^~…ŽÞÁÖâ:9›°_£@)ziÎ^2ŽÛß2›ˆ2¤\7dåœÀC"ݘ|!o‚m"ÜÊÅwT&Ü=*׈…Z)He ½Þ·þúǺ!í=ŠŸ²Ê¥)lÏ”s)Õ{Q˜¦Ê<ƒßW: ÂMZN–jDZ#å›K³€³(/€¢ˆP©Wè¹82­{ ¥g‹""jÕ+z "¶ ó ºÂ6/WÈc)p5°˜ø?7ÿ} ¾‘¤±¸88˜ŒUhD®KŒ`àœw0O°™„çidҮ݃¬Ç`˜ã×7°l„‰;*äÑ œ \ \èçÙŒœ{ðäK½HƒåYÄ ìNþ\Û•H$¦ó‘FC3òû+ûàà7Ôw ÉL`9ů3ȵ~©_53-× …üM¬.@ê8ùŽ“Ó]idð[Ð[ <<y‹¥(V¸×‰8É›0Hû‘Eòr1#4ôwù(eÏϯ=Љÿb;Ãù¤S†ö–8yÛ|&uÄIõ¦é7*3‹ µ&Â0Uv]a Ux»’%¢]‘?@V‘JÁÒ9†w__þ…Âõ  &³R¥Ï-lU/ÚK¡ wCjlå‰õÂtyç婵Î^3ãyYWKM¨Å‹yð¯ˆ-dzÀõÔÇBödöÇñeÒ(àKH„j¸øP™ýÿf³˜èž¼ ¸ NÅÞ!ÍÈ5Ì > X\ƒˆö¹ˆXuÊäÓZ_ˆ§$°¸ø6°uu(W_¦ôùÿòkd5—FÄs åÃ6·#–Ü%À[€—€ïßB⛄&à*¿ìóÈYö«)ûMÈX“Ÿw! §¡f rËñ ä|Ž}uˆ# ¶w ×qÒ¸¬†yˆÈÿ°¸ø&Ò£a±äa…û0 xà¹ï¡=%Ü. ˜5¸ÿäMù‘RBéµÊêcÝ23®°0/Ï3(m¸yÍL–-Cº/«]!ŸFl#”ÎOƒBár1äÃç›s2¾ˆ[°‘0‘¯;žÝá¡õ2¸T²*g9_ÎP'Ô¦(7¨U[è^ÓßÝ&´O4äŽw¬X"ªy–õQ>”Ï3ˆø½¶B>ËeÀÃUÕlð(Äâ9½Bº£ˆµ³ZëvÃ¥h*±}%ðUàµU”1PKû"亿±æW#Ô+¡+ñ2y#ðÏÀZ!ׇܥD[«¿¿Ð½dðYàfD”ro.Gziþ‚êÝ•"!UßNΪ>¢~Ÿ^ürO%šò÷1 î\ÃàcHƒ¶cy÷kð^4ÈônX,€îu#™í;¹+òq§¼•\)•5l‹ø.ÜALøÜvSDˆëP>Z+L­H¥\®»l*o½~:™dσ¼€‡…âÜ„…z˜@Ü#JÛøe«®r'ï[ã¥- rãYQdÒâ"óþkáh¯ÃK†Æ¸œ[~ýK\ÓbÖõ¢»_C©H>ÉKÿu•©1íU¤é¡¼+I/ðÄbZî…ߌÌÄúbÑ*Z€7T‘îà¹äÛëRâ8†ˆ¢°ø~b‰Ÿ2€rªa1ð~àFĽ`(Y | ¹¦ŸEzN†‚.D”—î þ¾°p¿½ójT‡ñ÷ Äm©ïEDûü•}!byÿà+HCf(¨fxÿP‡ˆ#O ÷W-9 é%Zü9°½Æù[F(V¸×‹8]«PhÇw›(ŒôB¡°TäŒó*d”»n@RQ6…ÖKä£Ã¯Á±b´·ÏãŒym¼ëMÓˆG©„ñ³ ? MpR²^èçžÅC&\R`™4ä“·.£p³>î’Wîd“?Þõzøûÿp8Þ±Hp¯ ¬ûù¦—ì‘u óH^Y÷¨à\mT™ZCºì+qÊ–àGwƒeÒ]ƒXŸwUQî`Y\\!þ› Ä주p“ßLý#D„M@Õòzà£Ô÷]t5âztCã:ÓGy×­Frç«€·".ck\¹ˆpNÿUd¿|ø;*»‡ ”vÄâþšÑù´kBÎíä»iÕ’(p+0qÁ98DåXFå|ç,5ÆÃ`ð²®ÙTC‹ÒÁ¢ý%¼M¬åÚ_—ƒ*û©•Ê?ÆÑÙ|t(_í„Öµ–cu.G+Ò76ÆûnžÍ„ñqR‰‚÷P Ô!_ßTî7P”?ƒªò…¸XÓ5íïÑ~ÃÁ -‘кïB£‚t¥’ÉKf)n]¥¥¤´ãŸÖ8y‹“[YtDg×Ç?6X‚<'o½09® ¯Hñ¼Âùˆn7T7ŽÐRc¨lír‘]I@ì~^E™Õˆê“e ¥ÝV:Ëÿ@„Q‚ò7_œÜ»aC'Úgðbädš¿ç"¢¶šß@ F¥ûìßÀЈö€YÀß#">Œ|ø"µí ˆ%ú†!Ê8|ø8C'ÚÃ\ŽüŽÇFð9í±÷a@œ îY =þݹM…Vä"2ûùcæÐz¸Œ"Vdc@+w¼a+–´és}—–{GÞë2ô%ÏâÒA9JçüÜ Î7üÝøV÷l}³ >5é”áuËa×a‡ûŸ†˜“;ŸR1é gV ׯŸ›‹É?6›¯êß;dh0¾Êž‡¢>–š2ÈXŽ.ª³Ž{ˆû㔟‰5‚XÝÊЄákD¬Ñ•XËÀ#y¤(/ÜüýmÀ'ñ7T¼€D~¹®Lš^DÜF¢mD~Ï>䯘ŒˆÓiTßÈX|øSj+†ª½¾K16±B~.âæÒè{{)r?‚œk×­ˆz%ñ 9ÿêpD›jiA¬Ò/!ƒ.GÍÀß ƒ^jüìFƤô Ïr]ÇRþyò›mÂZ|N{¬p¯É$FGDü*…ÖN^T™¢~îaÑWÌwºˆKFéÈ(ýdVp äìKz\qa×®êÀM{¾{GÈ&åZÉÖ¥´ˆwå¢ *šuÏ),_ˆ*óÖKᕃŠ-{ñþçž=ß‚khB' ³îBábJ¸Ïôû-rAÙƒÁÁ…3²¢š\Æ<«ãkÞLåaTéâyàQÊ‹Ia½X_e¾á"$¢L9º€Ÿ0ð •„eâ~ô6ÄMf0T+ðÒH”•kÉoN'küoÿýÀ!Dð$é_ÿ/{6b|Ò+R‰÷#á"Xe}«!IåÆÜdÄÒ¾ Äþ>äüÄç°¿},¹s\M.Ž}%Þ ýO¤·á‹”Ò ¼~ ñ«¢'1Ý/E VšË1ïfäÏ¡þ7âÞU-.r=ïAB­îAîã¢Áš‘hJ¯E&‘{ÅTihÖ3´¥åÄ ÷zâ¿jÄ墿_¹¿Á—¥aËp¾P,&γ‚1ä…ž‹oÄÒݯ(ÕOàö%]Îjåí×O!¢錗«C1¡®ÈùŸ‡lnx¢©@¤‡Ý`ŸÛC‘räú¨lViWÑÒ7¯R|í¿4=IñwÏ»fAzÿøÂk×½°N½e"Ø„¿ço–HB&¤k'`ª3‘(•ØHõ.IÄêþ:Ê»ªL®`h„ûeTvaØ <4ˆ¼3”ov;ˆÕúC”à‘(<ë‘ØáI?ßvÄ2^­ËH¼í~žÿ…éøÖÕBÜœö#½w#ÂøŠ ÇÅñþÒ0¨Ê ÔàN$ìe!i°ü‡¾Ô=û]$|à'!]‰Fdêï?A„b! þïÆëá"i‚²—ŒD>©&Л–T‘öTæíÈüÕ`ëý‘ó.w/oòÓ| Åù$¤d¡E¿Ëiîu#Ž Þ—Êà8N¿8îiSn›1~˜ðþÐ1yQdò ç-žç¾ß¹¿MkE*mho‹óî7MaRGŒt_&Ôp𠕨?øT“×Þï5]ä½m4Ç·¼‡-íás ¹È9„\WŒRþÌ« ”G2©X:Þ|‘æ‡ÿ#þÿŽ"/Nzî܃ÿó¯eéž r=-ðá(?¹|ƒ1ºNÄÁó{ <ãáXå~²hà*GÃèCÉ@¬}Dâ2—ãFàëä\jÁÄšZ©þn¤Û} ³X‡q ø%öïB¢ïü 7XÌ‚tEUÃ.¤2ñH&ؾ IDATþ'ç~dÆÄíˆeùfÊ7“ÏAÜž¾we†IQ>ÚÐ<äž-ü}{€BBVVjdD~ÿ'——RÙp1Ò˜)Ö“t‰Ý•ï©£ˆùH#àÏ©¯¼i =ÆÈµ/@¢ïT#ž»þ…Ò  bBæx ¹¶ÌаŒP¬p¯I0"Dµ?ÀÑ/_<‚„| X˜Ã‰| Ý;²~Ù*°i üh‹9K}n¿á-Wv°|q3©„KÎ $0‡ý×ýÊ„© Y÷p÷àðª’ö@øT©u9$å*.=vrx|“GC,×ó­cž•»xÏEá¾|W¦KL1×¥ÜáE÷9ÚÁhWœŒÁúÊœ47ï©"ÝFÄ‚>¶"⤒p_ \2ˆü˱‚Ê‘C5.3ÌB)Öpø9"HÖVÈc † 1ðÿ­{ׂ¹ˆ³-ˆEøÇÔf¼B¥A³Å,ÔG‘(,ÿRáØB^þ çq}…´íˆ Q!‡ëô­” 9q:i8Tjh^‚4’*Ý;§"1ÄÒ^MÈÌcÀ_"Q§;xú826`'ÒøªÁá–ˆîu$ø v´Æ‰8㔥ü²+…< §)6@³XxCô$ ׯjçÚKÇâe¼")Mþy+aaø°÷·)ß¿]7€v0ALJcÀ¸~Z1Š*W¢®dõrè]è…ªúÖpOi"ŽæÍçkŽðâ+Š–¥ŽÁ‰HD9Ôàe; DDëý(u .}y×™Báî_S椹±LŽ«.ƒXR3 ü‘P}åʇXÝk)¢×PÙgÿ!ªŸ\g  …i€ÿ‡X»k\žap¿O5ìB¬ø?¥¼ÛÓ Ä¾©Få¤w§±®þû Ë:Šø\Ϧò¸ˆbei´ †$Òp˜ƒôZ”c*2.d$ ÷+°•èþð•û]dpðW)îÞd9 ±Â½Ž?ÊŠˆÉ2ƒS ¬áEÅvÁ¾âô|«2…ÇøŸÉ¤aÁÌ8o¸|,G‘._!$ÒûÙŠ øÀbX»=ŒIC¦“8‚!&n¼„¬{iÀÅÆx.ö­Óžˆþ@ê*oþœF)’hÚšàª9Üý›&N$4-M'B,%LjÇâòo ÓFú]» êÉ6ò¯}±k¾Î…ÛGãyâ cŒ±Â}phD(ÿ-•géÎìÀÃ? þוfR½%{YN˜ Èl©åºc\ÄMb°ÛT YŒ!VÙZ‹öz ò¼ºLšéÈÌ¥µî.‹RóÄõèdØ…4dÿ•E:ù ð³“,ûby¿é½(…ƒÜÛ È=8RhF& «4æÄ ûû5.ÿçÈýùåçk¡Xá^gŒ18‡ˆ Í^šo9û¬çìØÅEwq—BÇ—™ZC*cÚ4o]3qcâ¤.yÖóìá`çQ ØMZ¸Ic¼Æë7ñúÀK`L÷È>¼#{}c¹KÖ0¥²sÂb?+šÐá“Úw©ñ­øAµ ôuiΜò*o=<óóÇpœP½ý뢵&‰à8±XŒææfš›šhnn¢¹¹…ÖÖ¢Ñ(±XŒx<ê­ñÆóü߯¼>|͵Öx‘‚ –0 é’¿ƒêbOw#~»ƒžFDÿ5”Có³©p_€„ ,ÇzDˆ—uá?„…ì:‰2‡“È€×rÂ]#.‘O6#÷h-ÊüOäïãœ*ÓoGÜ0jQöïßìJQˆæ"½ICÕÃ2œ‹ ­Ä}Àÿ¡ö>üby b$°œæXá^OŒ¸eh¥ˆDÒ‰&SiÒ¥ÿöD9áVw%H¬ñjCižª¼€ôP”‹[¾±¯Käýµ›½õ0"«î?Eâ«×‚4Ò蹑ò‘ˆ&"½S#E¸+äœ*M²Ô‡ŒO¨6 Ò@9|yþUнoåXá^'€ãûRƒSµ ¢½äÒåù¨‡|¹Uh)ßöÂÏ¢þÙ!7õ¾\öÚf®¾¸ 7í[¯M¯=ün×`\ŒÛ ™pbÜ^p»1^JºñKdõ«¦Ÿ!Ç(ü^TDD¸/ÜsñÛ ÂOªp¯„ç÷ÄBo 0n˜¬å=M&­ˆE ñæcì>4†'¶â½Äœ©Ê]±ÞÞ^zzz0æUœH„X4J<§µµ•¶¶6ÆËøñãioo§¹© ”4ÀLv0l~ŠXÜ# ž18§nçlÄ·y±'7©K˜bIŸŠø/Cb/CÂ>„ï#–Ì“l¸q'('ÜAüÒïâäDØ8à ÒA„ûPLúTŒß#bp¤„ŠœS&Í,¤ÑT/áþ2ƒ÷k/†‡¸§rŒõƒˆ·–f ±X í8x¾KMð»y¾{Íi6ÓùÀ7At'–ÄJÕ‡´òÑÞ†ˆŽvÄÊUMŒè0qoù$µ Ñè!×|™n¼ ‘nì“îg!Qjʱ¡‰_ŒðkF®‹L˜*ûçO roG-y€Ú¸W…Ù†X³+ ÷ç¨}/ÊAÄ‚_N¸kdvÛ‘Â%ÈdYåH nJåB€Ö‚cH¬w+ÜOs¬p´£‰D£/=7©P¾è rùv}É“üYט’~ï¾͸†¶VÍ_×θö™¾HNBº“9™Ãx™CàöŠˆÏºÅ˜‰C‘`ªÅü<ƒé¢Ñø¹ã¯÷Ï;OŸg ,õ€—ï½½çÎ=Á×EøÄ÷â¤]ˆ Pö3ۖɸd2½ôöörøða6mÞL,câĉLž<‰)“'3~ÂÚZ[¥‡EëþÑkN49Q>”ô „¿F^rµb"t./“F#!Àà}\/£üྠbý®×ÑNFþ„9ÝTÙHe—ˆZ4kíOß…÷JaLŸ§öD{‘†C%&!Úc$Äs¿”ʳÓv÷ס. qû+¹|YF9V¸×‹GÜ("Ú!‰àùÂ=4l²âàST1Wš°Ð/-ܳŸZsÅÊfæÍhÄM$Å’ž>„I€ÌqŒ×+á³29,Ö–6ÈÊHv¾…=;骂à9®‚2*óˆe?4pÕ„ÜlÂû² å`€dÂð¦×æ‘“ùå:Es…Gp5¢:lA/ÜžL$èììd×ÎÄ7v,Ó¦OcÚÔ©L:•ˆ#rÆór½–Z°qùµw#éBÂ=®¢ü3ób`1ƒ ÓØŽøÉ—ã"öêÅ‹Ô.AæØfÜäp»ÁëCŒ?þ`Ò¡Âx!á^ä0*ö±˜˜G|ܳá!}z…?à_ÌCÚshˆ¥ùë7â•ÃXû2´5T¡˜h/e!/p˜/ìÕÀqȤÓìß¿Ÿýû÷óBôñ‹;–î®.´Ö§ÓàÔ¡ä8ªð_º¸æ !ÙÞƒóRŒnA&(R9÷#ˆå¿¸H8Ì¡îþ ¹ÖS¥ÙH¸¼iþöfrâÝ -S«È·ÜÀÊZ²q«5idD%jÙ#f?Ùð`%i«°ÿTa†¿Tâ·C]‘‘߮ҽlÅXá^7 Ž#"ÚÓºLt˜Ð!a_2}‘¯ýüµvȸivï=ĘØAV/îÆé:B*‘D~„g1BÑŽXÇî*h#„?)¾-õÜ">ìRàÂí.}Iͤ¶>wCïùV G{ 1jrn7Ù(;Åcµ›2פðØB‘¯ù \Ïåð‘Ã9z×ue†\oh¯õ(Æ V§û‰Ncð1Í«eÒ^N¸+Äj>‰G—¸šòþÕ.ð+êg¥L"ƒ‰Ob1_ŒŒ—X ž‚ˆôFê'¸kEŠ¡ ;éRÝßÂPùë@å„yc…ý§ ã‘Þr$€gëP——¡iðYFV¸×tªãDˆF£"àBñÐó––p‡ÉV ¶I—õ§ö<úúúØ»o7[¶î$Õû*ÿô'šÉ­è ,Éu~~šœ«L¾› ¡Jÿmý,òÙï¿m㇊ ¬ö8(åÐÝå¼ùG¸óÚ8þ³"Aç„)bm/¡©‹¥ËÛV$¯ Á¥)Ðu½ò!n,¥8Œ¸ÄÜDVYËÉ…š(?F¬îåÜ)– ƒÇþcùŽõ)'¦62ôQ+¸œ\ÈÉZCë^‹Œ/x •d4 É=ø‡&ïá"÷rN‰16IÔp1i–£“Ú0.‡¡¾Ï<Ë)ˆîuÆ@Öâ®´&/hc%ážu“Qy"W„nnR!×u9zô(;vì`÷îÝìÙ÷*==)þþcí\x–"8EHšÐgÈÂLTšg‘/vlÈBo‚è6ÊŸ‡‹Q©d”[Î;ÈK{&óíGZÃó|òŠÌ³¾÷æý¶»¶á¼CÁ H¸½rS»A¬î¿ úÙI—"Ó”ã—Ô/L!ÈïUÏòÂDËú›×£)ÃTËÀ ‚§lšáF!=g• }o_!§ÀËÛ2œXá^GŒñ0ÆC;ÑX'kq(1ð4›]å¥Fi‘þN$B:bÇÎlÛ¶={öÐu¢ c ½ ޾™w^¯ðÒæÔôÐ0ŸøyC¾'äÚ-Û‚°™&ˆó—´çÐ÷øä5GXßÙÁ3» ÍñPxI“›Õ4ãÆ˜²OÉÜïv•ñ×C ,0ÙüÀȽpÊ¿»jƯBü‘'!>®ˆrpÁ2Hä.àbí݇¸Ä¼Ê©1Ez/5æõ”~v*dë4ª›dÆA„~¹ˆ;ˆ›L­\+ªiPx Ï5_ |i†òO›?Ê!b´ˆJ Z‰½ÔoN‹°Â½þˆhM4ÅU:$ÆC¾í û¨(ñ:ç;â8­4½}}ìØ¶Í›·°{÷n‰ÆGÓݯYå3ïj ª É4#çÑZÔ&Ï]†œŸ|v£JLå€JùŸ.½ ×|œÏ\ßÀßmåX/Ä"AY*ßÕÅø£‚ –÷=”¾X/F~”þÐy^òà(âÖ²i†M±°5+ˆ5g4¤ÓÏY+¼^tÛÒ¥K×nÚ´iƒçyËËd½páÂ…«7oÞüDèЧ o»ë®»&}ò“Ÿ\ÓÕU:Túœ9s6ýêW¿š´lÙ²Õä®›*X7oÃûÃ×YΧ>õ©e_ùÊW¢©Té÷X,ûþ÷¿ÃM7ÝöÕ¯æÉaЬ—Úf\×ÅquðàAõ¾÷½ï‚{î¹ç&×uOÚ®dÂãÿMJ'ž1:ô›÷Ckí\~ùåSxàYHCÒ -nè3X/v^L:5²ÿ~e*¸Ã544¨¾¾>¥²“SÔ„ÓëÉ2t8TaèC3VÁb)‰îuÆ“…3)X*Ìcž•]d`$A;½½½lÙ¾• 6²{÷n2™L6¯ˆVô%=ƶhþêýÌœÉFŽh/Fø•äÛi 7åN0làæôôŹxÁ!þäÊñ«8®§ˆè.Òë1Ð}ò½Ô—Ó†°{L „*⋬°ø,©¥>ƒu‡\8ÀÂÏðRl[Ñå©§žŠ¾á oè|ðÁË ÷ȸqãÞìØ±±íííAC¤Ø¯=º0•J•Œ¹­”ò®»îº½Ë–-[@mD™3f̘±*~ªx¹ª©©©±&™tGïÚµ+~ûí·_ó裮r]w@¾ÏÑh4ÙÐÐÐÛØØØÝÖÖÖÝÒÒÒÝÚÚÚÓÒÒÒ×ÐЊÅb™††† Àý÷ß¿rÿþý³Jå‹Å¢W_}õJľÒ9Šøl£óŸÿùŸÇ¿ÿýïo;zôhɃÛÚÚZ¾ð…/,ÆÌh—]2¡Ï ¹F­)Xï÷ùÅ/~1ò™Ï|Fgߥ±b³<šÊñÛAqÛð`–ºb…{1þ?ÇqˆÄb(­ ÞéY?u¥roy¥òÜ-´Ö8Ž&™L²mûv6lØÈ¶íÛH&“8ÚɦSÒð<ÃßÒÄê©^3²E{@¾gJŽ`W#穌";@5χÞÁ3ŠÞ>‡[Ï;Äó“øéºÍŽÉ^ó~e!¿ªz_9yåöGa¬¢hhhˆõõõµ“ßa1¬ >£þêH‘õbß Å¹ö?s-¶|ªÝ–·½¹¹Ù¹ñÆ·=ú裉t:]òžqãÆ%O=õÔØ+¯¼òp©4étÚ<òÈ# ’ÉdÉpmmmG®¾úê]ÈyÔB ¤¿Ç0°ôEïÚµ+zÛm·]÷Øc]ZíA Ý'NÜ?}úô=Ë–-ë\ºté¡E‹;묳NtttdÈÍÞ ïì³Ïž[N¸¤¯DpϦ×ÍÍÍãÇ)ûnmiii>ãŒ3ÎfSüú÷mÐ Ä|XÜg ¾{ï~÷»Í÷¾÷½/¼ðBÙÊŸy晓֯_?³ ÏÂÆHvŸRªÚ{o´ˆXEuúhÔ<È-#+Üëˆ1bqDbÑÕßÍ¢¸Õ]FH&Sl|é%ž~úöìÙK2™@k-dBægÏ@WŸÇ‡ojå7Eq3§ŽL¬*˜Ê`ayfBÂXhr“4‰qÌ%Jc$Íç®;ÄÁ®É<¸IÑêOi10kz¾?(¿Ô€Vå߯;u~“BŒ1‰üˆèðzð=Ä.¸à‚ùk×®-ë°xñâ™ÀÈ·œ‡{x)Õ4+çvQì{à7_k¼[o½µóË_þòÎ;v” yâĉñßùÎwμòÊ+.‘D½ð ­Ï>ûìÒr…-\¸pëš5k3zQ€êîîV7ÝtÓëÖ­»¨šZ[[¬Zµê‰n¸aãe—]öê¼yóúûÐ,…¿»J&“*NU­¼ûÎTò‘ñyžÉåþйqèx<®§L™2±’p¿è¢‹–#3 ‡{ Ý‚²ßÉNŸBzaŠ.7ÜpÃÔ_ÿú×Ý„8õ¯¡:ßõ(£Ã$fAXá^Oü׊ÖÑHÔ\šß3¬u¾hŒD"¸—Ý»wóä“kÙ¼e }½}(­ÅÊ䢧ÏcÙ¼¹%BÔä0RJÎÉÑ sˆ7óÍE\ÿšhÝ*2®2Ü{ÑCD¼èD:ÂÄÖn>yõ Öïiçx|ös—óù½ç…è,Q±‚ã_÷ eJzO Œw’Â%xᇗ8ò²‰Ù^ŠùIæ™={ö´§žzªì‹:7 ñˉî@ÄœêxãÇ×+W®|±³³s¾ëº%Ÿ¡k×®=óàÁƒutt÷Ýwß´ƒN+u|$I]qÅ/2:…ú«¿ú«åO?ýôÊJ #‘Húì³Ï~êÎ;ï|ø¦›nÚG¾Ïþ©>ûf-(×@ ãc´çU5³[`Qvè•û^ØH »ê8W\qÅÌûî»/šN—Ö¼Ó¦Më¸ûî»W­X±â(Ò& Éжà™Ð¯\ÇqT§:Ø#ê¢Å´02B[ZFV¸ŽÖDcQ_C ô}Ý#‘Z+öîÝǺuëxö¹çéêêB)…ã”6¥2í­š¿|SÇ;$û†Ø°d¯B"Ýå(ðg;íIŽÍй§—›LÓ0sŒm„¦¸îyñÏóç&ʺþP°2Â3ÓêÐc< ZÓÝ×ÀYÓŽñÑ×5ñ·÷Å1€õ„d‹Î³Â–Q _ÎZo Iç½u|^Ì$ü=<¥{Œþ<^°­œEh°‚0ô«W‘XT}á`¾‘Œwûí·o¸ÿþû/9räȤR‰^yå•?úÑf~ìc{™"ç~ÿý÷ŸYn°ä„ öèCÚÄ(´¶ÿö·¿÷ƒüà ÏóÊZÁzn¹å–_ßu×]ÇãA¨Qw=†ƒB!æò“A"\Ub"òŒ=g¶ŒR¬p¯ b ž÷hT&úS*'Û ¾¨Féíëcà xøá‡yå•W€ÜàÕbÖM¥Àõ ©´áη5qÅù’}µ²ëæŠV€ÖàD5ÚQàÈ“„cÝ.{gؾ³Ûúxig’—¶w³igо>hk„¥Ó`ñ4‡ESaé4—y“aB+´6@Ä‘r\Ò!1œLUçã½ëÓ]…t‚qð ô¥o?ïU^Ø=•_>ïÐÚ@ÖW^®;ä½[ µVÐ0(Óe/æ Æ¦L™2öÀ¾= é/¶Ã¢»Pœ~ÛÙÚ|–ÚVŒÑ"¤ë¹òÊ+.Y²dóc=VR¸§Óé¦x`‘/Üèõë×7mÚ´iQ¹BÎ?ÿüçg̘‘bôýNæë_ÿú²ýû÷Ï,—(‰¤n½õÖ_ûÛß~,8®u³ C•nBáô”ÿM#ÈŒ¹P¤`Á‚s* ÷3fLúüç?2'AÒXöƒAšÜxLø»RêHç1y>wW‘¶VX ÿiŽîuÅ€ñp"¢‘¨¸]hñ:PÈŒ§(غe+¿{ðA¶lÝJ2‘$qrÇ—Àó «×ã=×·ð‘[cdR^6ßZTZ)¢1 æ›×ÓGNxtîKòòîÏoîá™=ìÚ—áб ‡Žy44gÆôÙ,9c!7¾ã¦M›Æ¶mÛøÃžá¡í;øáÓ¯é;Âäv‡q-Š…“aùlÍkfxÌè2­Ý£­Qô¶ñ ™´ëP:§¯œ@ò›äÖq㟻î{NtðÔ%e^‡°ÛKx,jIËz."P0y–ça* æÏ¬ U%ü"+õ9ÜIF;Ð7ÝtÓ3ëÖ­;7•J5MdŒZ·nݲ§žzê±óÎ;¯‹œ¥Xýë_?ãØ±cãKÐÒÒrüï|gyG呉Z¿~}óo~ó›K*¤3çž{îSV´Ÿ¶T2>¨j\‚ZZZšZ[[çѶѼÁ·ôìkÞûÞ÷NûÖ·¾åùc~J1Ò¤IS8PrzÑT¦Ò2Š±Â½^$ÀDLvpj4ÍF# f<=~ì8>ø O<ù$ÇŽóÝbtQ {!]½KçÄøÄí 48Šd‚ú“uw‘:¥AÐ6о—]ûRlîLñâ¶^ž{©‡-;ì>àâgÌØ©,9ã Ö,=ƒåË^Â…ó™=kMMM466úâÕ¥··®®.^~y[¶nå…õxqÃFžÜ¼…‡¶Æd’4:)fUÌŸª9k¶fé4܉Ú24Ǥ¾®Q£ðüÅå«_'8%Ÿ!І§ô¥£LjKñÙ«»ù㟵²÷¸¡)\šü¨ŸzaH‹{ñþã[Üý‰°‚xÑ#‘ª,lALmF—øro¹å–_ýêWwlß¾ýŒR‰Ž;6áG?úѼóÎ;ïyrçï­[·n^&“‰–:nÑ¢E›.»ì²W©ý½QµUÔ³…ÕöwSßùÎwœ8q¢d£`̘1‡?üá?ê­É5¨&^ºëº'{ÎFk]Íß¡rjy}‡óo2p‰+‹çy'{o@U;Øu]—ÒaMÃ.ˆa"Ë—/×Ñh4YªaîÓzÝu×½ç›ßüæ#È`ß$bÍO >òÉж td8´§€ûÈX¡á˜˜Ìr a…{ þRµ–™S '*‚rËæ-Üsï}lܸÏóÐþ ÕJO&$S†±mŸ{O 3&i’=¦¬h÷ ¾âîâÈÀQE|_\—®>xõhŠ=¯$Øðr‚ /w³öù.¶ìréM(š[ÇÐÑ1™Ž‰“yÓê3Y~ÖkXqöÙÌš5‹±cÇÇK–¯µCKK ---L™2…‹.º€ÞÞ^Ž9¶íÛyöÙçxþ… lÚ´‰ßï}•_®?Dª·‹ö¸Ë’i^;ßaÑÅâiÓÆ)Æ5§iŠËùày¸žGÊázžQ~ïFØp’oDQÊГŠpÞìc|âŠ(úó\£È;¤P˜—šx©Øvå[é• ¿­Gº ­Jú†±‘|žE™4iRzõêÕÏ–î©Tªá÷¿ÿýbàü?ÿçž{®içÎ%ÝDÇÉ\zé¥/¶·¥ã$ IDAT·1¼kÉ@ò’ûóÉ'Ÿœïy^Ùîþyóæm½í¶ÛöRß&UA;Ü £Á¸«Ô¬ìêÆÅæ•=ØòÛ˜+,¯T>fÖ¬YÇz*wÖ¯_¿ xÕ…y†Ï3pÑ D|qßÉûN~O@6òoΕc9M±Â½žø¾Žvˆøî2ÝÝÝ<þÄÜÿß÷³oÿ>”ªtÕ3Ï2®áConáÚ‹#¤ËŒƒ7Fôc4"¾éDxŠDÒãða—½ûzÙ¹'Ŧí=¼´£·ö²}w†¤i¢£c3¦-áš7Ìeñ¢…,\¸€3Ï\ʼysin®MÏ]SSMMMLŸ>Õ«Vpôè¶mÝÎK›6±eÛ66oÞJgç.¾ÿÜ~Žüî­‘.N‰°xJ„ÅÓOÕLç1uœaL£¢1.ÁPÒCÚ5¸žƒA¡‚–K1ÈÄXsæq~·)Æ=ëZâPhqÏ}WßÛ?Mà¥òäú¨Ò±§%7ß|óæ_þò—Ê Rݵk×Ìgžy¦yÅŠÝ€zâ‰'&”s“éèèxeÍš5;‡¢¾mH Geß¾}Ë¥ÑZ»Ë—/ßA E¥ëº¤R)û¾óÎ{ ÕþæÕŒò,tC,u|¶WìÜsÏ=ÖÖÖväĉÊ•ñòË/ÏݰaC|éÒ¥=ôoŒ„¿GÍÕR°? ·Y87¤Œ1ÉéÓ§_¶gÏžj^¸öå2б²º‘À˜žç‹Einjfë¶-üè‡?aý‹/’N§qœ¹çLuœèñxÇu-ü¯·7àºà¹&;€S)ÐŽ%ÅÑà€IuÙ²'Ás›ºÙ¸µ‡M;’ìÚçrôD†=M­,Y²‚K¯YÎ/fñâ…Ìœ9“ñãÇ1fLæ©)cÇŽãÜóÆqîyçJ¥8qâdçÎ]lÞ²M›7óÜó/ò«û·â%ÑÚmšYã=Lv8s¦fét˜>Î0¦Iu<2ž"ãŠX7PJ“r¡!êñ—×u±÷x;ÏîV´Ä+G)½.ß1yñƒŒ1wH,nõ¤Úºôó,…yýë_påÊ•OÝ{ï½×—JtøðáÉ÷Þ{ïô+Vl܇zhN2™l*•þ‚ .xöŠ+®84$5€EvÜ)ôÚµkÛ»ººÚÊ&ÒÚ]ºtéj‰èرc‘d2Yq&ÌXÁ«ã>d÷ª ]ÙU¹¯Pþ™0û³r¥ŠŸk±–KÞþÉ“'«¹sçîØ³gÏ‚rÑŸŽ=:åK_úÒYßþö·å[¬±P¸^¸ÍŸl„FɳÜïÙ³' ¼‘ ƒSµÖjñâÅã7lØ0 ÿüH<Õ^3Ë©îuEþNµ£YûÔZ~òãŸðòŽ~Å`ÕêP ºº ç.iàsïn"7¥ˆ7*p4x‰”áx·Ç«ÓlÞÕǺ]<ób_Ns¢×E;ÍŒ0…¥+–pÁÊóX¾|óçÍ££c---Ùè7§ ±XŒ &0a–,YÂ5׈˜ïêêbÿWÙºu/¼°žuO?ÃKÛwðÔÓG0k{Q&ÍøÆ g΀e³¢,›¡™=Q1±MÓ7D# Qd ¤Ý(“Û]>{Mwü¨‰ã}Ð…<«z¿©ý·çV¬«`0rÞ3{$ Zãy^U"…‘}žåpÖ¬Y³ñÁ|]"‘(:ªçy‘‡~xÁ§>õ©{öì‰oÚ´i%,~ñx¼çª«®Ú‚¼À‡"FyÕ¿A ü½û•}àÀ†T*UÚΧ­­­R¨À ¶mÛÖT©Á⤄{ÝEJåY‘¡òq¯qT™RÇî50Ÿ…ëŶy—\rÉKO<ñĪr³$»®{ôÑGW;vì‰ööö BQοÜþ°ÿ{^Ãõÿñ—¾úê« JÕ#@k­gÏž=˜ƒÊ—>9†ió#dN¸KzÜfçž$/mëá¹Íݼ¸µ—Í»\Ò¦ñãÆÑÞ>Ÿ×œ;%‹qæ™g²dÉb-ZD{û˜ŸiýˆÅbŒ?žñãdzôŒ%¼ñ1~;f×®]³K¥ëèèØó–·¼e7¹n­©‡¸*™_OOãºnÅL‰D`ɨEÙæ¡‡šrüøñ².ä‹¥A ÷jŽ5>'YÖ Ë‚F àÞ¢s/ZT¨ŒB¥_ªì`»û¶·½mû¿þë¿8tèЬr…ìÞ½{Ñ]wÝ5÷ÓŸþô6òÿvKYÚ ëWl §ñî¾ûî+Òét+Uà‹î`6ê ¯`ò½Àš¾.91ŸÒƘ~‘vBÇYa?ŒXá^G\Ï •bݺ§ÑZûV~ï{žAo\å°kï þó›w$Ù¾»—{ú8|ššÛyÍk–³ìÂ…Üô®E,Y¼ˆyóæ2aÂÆWvðèh¡££ƒŽŽ.ºP¿öõöräèQ<Èö—_fëÖí2vË6~z÷V¼¾ŒoSÌcÁ´(ó'iMëå‚ÙšÿÙæ`tÖŠLógT Ï• )]äòzfH^žõ¤êéÝÙçY3sæÌÔE]ôÌž={JΤºoß¾™÷ÜsÏ”7N8qâDG±4Žã¸«V­úÃøñã]×uã8C)Ü+æ=VYÇ]­uYS©çyzÇŽ%ádËVû÷ïw~ðƒ¬r]·ÚîÓî!Q^–¡´]eÑÙäµ,»Ú¼ î«“)¿ò´©â’Žä’·»Ø!á/‹/îYµjÕã?ÿùÏË ÷d2Ù|×]w½aõêÕw­\¹òÅ-êÅÜhÂi¼ßÕí·ß~aggçåê=ct*•rBÇ÷‹dS°ûƒ;ÁÌÚáýnèÓ\ÿºšÐ¾lyVÔ-V¸×#"å¬ìƒ7ê$z»øéáß~áqäDÐ,\¼”w}àZÎ;÷\,˜ÇĉŒ;–ÆÆ²ƒâO›š˜ÖÔÄ´iÓ8묳èîîæØ±c:|„-[¶±ö©u<úûÇøÑ[ˆ9†ñ-Ç9†ë6¡bMî/¯{h[¿é^Ãn4¹ ˜B¿ýH´é«ÉçY ï¶ÛnÛpï½÷:~üøäb 2™LÃÃ?<§³³³ä Öæææ#7ß|óÀ"ѳ”Õ\¸O˜0¡/–eÒó¿ª3Í?Rù>_ )vLèØR|Z71G‚ucLÄ£ý¥–»Ÿ6X‹{=1¦&s™*ZÛÆÐÜâây®ëâf\v¼¼ƒíÛ¶áyžçÑØØÄøñã˜0aÂÿß޹ǹQ\ùþT·¤Ñ¼5¶gŒß0b &ĹöšÇ5á¹`‚!<Øò!Þûá³ Öönn6$$7χÜÝ% ×$!„…½fI ÄNl60ÄÆ&ÄãÏÃöxÞ3šI­®û‡ÔšR©^Ý#ÍŸ¯?²º«Nªni¤_®†ùóçÃâÅ‹aÑ¢E°páB˜5kÔÕÕA]]ÔÔ˜^³5µp]úúú ¯¯Ï[†ššš ¹¹ššš ½½º»»¡··R)B!ˆemÙPQQ!ÛË e[`YÌŸŠ÷lJŒø.µ’¼öÌwQ™`è 7ÜðÎûï¿ÿ_d) d-ËJ­^½z÷dº¼KÒ9sæ¤fÍšu´µµUz=@GGǼ‡~ø³7n|Se§À~ñÅëxà›zzzf ŽR;›b0¥ñ1)+z×EhO˜í1Ã\<Ï?Ëú/؇ÃäÞ{ïýÏ={öœÓÝÝ=OÓ_ä•W^¹þÊ+¯´^~ùå_Ù™Ÿ×½üpS± ?üáç?úè£wvtt|F}”‚QOŒDÇ*+çÇÈ—‹ì„Câž½ 9ñÞ¯„J)%‘׃Â}¼ ð–,\Á$êµ, X„æüRJfo@”J¥ ··:;»àƒöB:‰Î‡ÃaˆÅbÐÐÐõõõÐÐÐ .„ÓN; æÍ›sæÌ††¨©©H„¿¡Üäcddúúú ££Ž9ÍÍÍðñÇCkk+tvvBWWtuuÁÀÀ¤ˆeA$P( ¡P*+«À¶C`Û6+9'„€g¿M€ð¯‡ziHÈFä@f}~&~H]ÊF/¦*&y¦l”çËÝwß}ðŸþéŸö666žë·íܹsï¸ãŽ?CéÏ“÷í¨4¢”Bö–òEÏ%—\²{×®]A·’O¥RÑM›6]þÌ3Ï||ûí··ù˜Gyä´'žxâÆîîî٦㢔Bæ&›c;ælš€Î†2¹×Åúû'žo­aFûµ%Y¡¬|sQJ½Õ¨LóÎ¥~´ʈA6Z,t¥(£@®»îºc[¶lÙô/ÿò/N§•_Š©Tªâ׿þõW–.]:ïÞ{ïÝüÕ¯~µ‰ñÅŠ[>§ìÛ·/úÀüÅ–-[næïyàN›6m¥4ÔÓÓs–ð`(µ‰D„9f^¸»’:Ó_úDÇ¢³=?n토pGLb¬Þ*¥£9ÔüÇ …ŒP'„€KÓy«—X–Ô"`“PæT’‰œ€Ì]DÝ4¤Ó. $ ñàAøÓG7íB(‚šš¨¬¬„êêj¨¯¯‡E‹Á‚ `Þ¼y0wî\X°`Ìž=*++¥‘äRB)…¾¾>hoo‡––hkkƒÖÖVhii¦¦&èéé¡¡!„ÁÁ8¸Ô…p8 ‘p"‘T×Ô‚m‡ d[`Ùöh‹·ð=Íœ[/ý4ïÕ’oNܾ^b>û#"eþ!ŸjkkÓ^xáÏÃÄx¬X±b×i§6 ¥ŸÜLô›Î½óÎ;÷?õÔS-º•:úûûëׯ_sww÷ ÷ÝwßÈ?§lä.Çï~÷»ºÇ{lù¶mÛ.òukx×uíáááÉó“Ç'Áuüë©}ŸfÓ}Lú2Y)HmgÚ‘üÇܵuëÖ 8p¾I·ûöí»ðßøÆ7n|ó‹_üâ»wß}÷ÇÓ¦Mó–x-Ëo~ó›ØO~ò“O¿ùæ›+=ºÔqœ‚‹ÓÂápÿ—¾ô¥ŸoÙ²å¿Ê„;ÇqØ éL#î~~ÐùäQFâ½h»¢=€Â}œ8Ö×çN¯ªÙû—ìì>[&+gE¾KÝÜ <Ÿm¡—jFs¬fs-ÁM§ÁI§¡`ººº ñàAx÷ÝwÁ²,°m;oýôyóæÁÂ… aÉ’%°páB˜;w.TUUAEE”••e¿t"NÃÈÈ C?´¶¶À¡CM°ÿ~hjj‚#GŽ@ww7ôôô€ã8™ÕY²©A¡p"ᄘQ_™Ë)÷VðaÏSî“&{žˆ r.\9F¶–»à¦LB;`ÎùÔ_ßÜϸ§òq³fÍš?¿üòˇUi1³gÏž¥ÝÝݳEÑüÊÊÊ®d2Y)[“›Rj …`lpãë>J¸¤Û¢ömºbQö— ^ôÈ¿RéÇä"`ï»AâK&8Eû4‹Ñ¿ÿû¿~ݺu3:;;êúœùÖ[o]»k×®Õ?þxãܹs›çÎÛ^WW×W^^îô÷÷GŽ92£¹¹yþÑ£GOÇã³Òé´tÉ·3Ï<ó•»ï¾ûÃÍ›7AfC)µ²«[ù‰Ž ]qu&Â\61ЂÑvsP¸—J© £Ë*…|ðÁ¹Oýä_-þC]˜#À¤\'óY äÄ9g›—.¤œT*ánƒ÷Þ{?Å'™´›†úz˜¿`œzê©0þ|X°`Ìš5+—G_UUÑhl{4}Ôqœœ8ïë냶¶6h?rZ†Ã‡Ccc#´µµAOO¤Ói „€mÛYnA(‚h4 –Ûʤ¸°~"ÄÿªjIÎ{î9÷·»g#8ï¢;ª²ûü„æ°L".™]¹re÷§>õ©ü÷yóæí¿êª«ŽÂøœ꺮ÑÅ©¥|ÝÖ­[÷‡;v|¦¹¹Y»ÚK<mß¾ýŠ7ß|ó¢Y³f5MŸ>ýx8Néïï¯êêêšÙÛÛ;Ëqi Cmmí‘[o½õÅçž{îjY¤ŸRjŒŒ„alÇk¼$è…SàþuF¼Ž»*²ëa´Ô£é ¸¾”Ñ_…{ã7Ù¿ÿ³ßûÞ÷¾Çu÷ð ©Tªº½½ýÜöööswîÜÉöaež9sæ;ßÿþ÷_¨©©I…B¡”¦?oª:Ç¢sbU× Ó îc$1*€JæQ}Ž@¢]tÑì}úË¡~RMtâ\VžMxô ´öl´?“ãmMlˆ„#å£ÖÍæÑ»4sQlwo/ttvÂÛo¿ Žã€mÛP[[ Ó¦MƒX,Ó§O‡¹sç¢E‹ ¶¶6³ŽúÁƒpôèÑÜÅ¡===088™*…ÂagóÏ-+“Úb[£é-yË- 4?½ˆ9N‘ –‰káPEÑxOÑ6?‰¢i`jç¸Ã… Ç ¢¢‚\}õÕïïØ±ãÂd2Y¥³/++¼ì²ËÞÎ^”:^ï£~JymÂç>÷¹þûî»ïßî¿ÿþ¹###Fws§¼¥¥åŒ–––3üôU]]Ýñ·û·Ï\sÍ5-/½ôÒj•m___Æø:¸!wfU™¢ç¸Þ͸¯-1ÕM1ºk3!„fSed}™DÛó¶~øá?E£Ñ=öØc·uvvú]í%ox¦†Ó¦MÛóØcýàâ‹/îknn…B¡•ßD"á½Yq­Êmå·ƒ ÌDìƒÄui wC²Ý€*¨Œ0¯€dÄy2ö0d–=Êû©R⺂4S!n’‚À}ÈÄ©¢Nå›BæR¶eØ(ó&x@]œtÜtŽë€ÖÖVpB¡LÎ}(‚T*Éd\×…H¤ "‘p6½¥l+U÷î4J ÄÊ<{¹á¼˜&^‰"ú-Óâc‹XÃۜש‰Æˆ{!îÚµk›~øÃ6·´´h£É ‡n»í¶`tå‰Rc-QTÖ#½nݺßzë­_|ñÅS©TIn6‹ÅÚÖ­[÷ìúõë?:~ü¸Fã*ûx<^žL&!‰”&)0ú:=êíÓvÜ#îܧ‚Feµ©4¿~D:¿Ÿ·}ÿý÷7ÖÔÔüóC=ôß:;;ù‡oêëëÿðÀsíµ×vAV„Û¶íê„{öÂV~âÁ¯gÏn«Îì!²IRDNXážéÍC¯€Œ@¯€ŒH¯‚Q‘žk–}&Ì>»L™HÀ Ôé'•(ªËv˜—b!0ã׆—ùã}8’Øf_Õž?xv¥;‚P8”‹dÓ‚Qúc/ %…J:Ñ&‚6y~$õ…íEb›(ûæ·ù2‘].7žïuœòÂÝq"ºÅu]ö¦6'îœ9s:mÛN9Ž#îµµµGï½÷Þw¡p¢_Jh*•"ª ¥ÔN¥R¥ÝèOúÓmëׯïÚ¸qã•---gÕa,k¿à‚ ~ÿä“O¾vÊ)§$™UWW+oþÔÝÝ]700`•——›.?ÉCÇÕëN§Ku~©ëºÚÕ’É$+B‹Ù·÷÷.%N[àÿ³ÎÄqmÀÀq›É\2î|¿'x׬YstÕªUÿçë_ÿúÞ-[¶\ÞÝݽh¬«£Bh,Ûá…þÛÆ[UUÅ®ÕÕÕiBˆ*Ç’Éd%äŸã BÝ$˜`"âu“#Ä''„pÏþ!U0/z…Ñ<ô0òE9d-ÐÞ^WœÍ¨æu]×Í^ðI)5^yE–JC)'6}¤Ö˜¶ãŧ.o;¯œiËm2v’(µ"åDT&·Þ§dÜŠ4™<[ɘ =[gY@˜÷@ r\ÇX,6P]]}Dö~v'TSSÓ 'HŽ{²gÏžStë=¯\¹ò·+W®ìƒq~ýkjj†c±X["‘¨ÌæÿæA)%‘Hd¨ªª*ãôº}ë[ßÚ{Ë-·ø‡ø‡ó·oß¾¢½½}‰ÉÝN=lÛNÎ;÷à .¸`×í·ß¾ûòË/ïšN§Á¶íÜø/^ܺoß¾vÑšÑétÚ¶m;ÕÑÑjhhPF4UTTT¤êêê:(¥¶mÛ€t:mÇb±ã•••Þù-jŽ{]]]oUUÕ1˲\>m‡RJ\×µb±XJã^QQ‘¨­­=â8N™è½•N§íººº®P(”Оˆ®­­¬®®î „8¢ÏÇqB±X¬;‰8 ?ϲþeÑãQ___ŸúÅ/~±í7ÞøÃÓO?}æo¼±¼µµõÓÉdÒ× #‘HßÌ™3ÿxþùç¿q×]w½ùå—÷‚$ÿ|úôéÇ¢ÑhŒNús㢔†ÊËË»{zzH]]/ÔUk·‹î¨ªŠ¸‹&8ªÉXA9æ·cL3ÃÉ3Óe/õrÒ½ucÕ{T*‡‚ááaXvþ²_oyýõŸÂî(ß·o_u(~§R)kþüùCŸùÌg”) Ÿ ¬ßþö·±ë®»îôööΗUWWùå/ù¿.»ì².ß 9räHx÷îÝ5©TÊ}qRJI8v?ÿùÏ÷ÔÔÔ<ÁرcGõ¦M›Nþýïÿ©Ã‡Ÿ2888-•JU$“É0¥Ô²,+‡G"‘ÈP]]]ǼyóšW¬XñÑu×]×|î¹çä-+˜GSSStïÞ½Õ"ážJ¥¬ÚÚÚÔòåË{ËËËÿMÆãqûwÞ©íïï‰þ.Ç!±X,uþùç÷WVVýüîÝ»·òÀU¡PˆòÇH)%étÎ9çœþ“O>Yuc :;;Ão¿ývLt~2ÇÞÐÐX¶lYÀërmãÖ[nýN"‘¨…ByK#ð_ÞS0±njk÷!šÙaëÖ>ùÑ#H½lb‘«×ü Šô›œ'B$ 8t茌ŒÐÏ/ÿÜÿÛ¼yó/a w0ÿ 9Q> ­¯|å+ñÜsÏýµ*å’K.yþµ×^ûw6<Θ¼n:¶d2 ]]]ßH¾ IDATá={öT{k¢‡™g‘0¦Œêlkœ­JÈS® ¤¬¬,Gâ”Æg°Þó7÷áÕ¶y¹ Ê«°)°clMû`õ¸Ð¿'€=#_]¤:o<º±hë¡ ^—ÛÎGÛ…mEÑýÑÆÎ‚”ã@:íqgΜ٠S_¸# ßúÖ·.9vìØ•Íi§öî]wÝÕùŸ1‚ŒSnR§ ²è¼JЋ¢Ñ²2“‡*_ݤÜįllü1úúCÑ>6&T¸gsÒ dÄ8+Ô+`4=£ùèÞ¬0(çº?T^p›´ñìLÚäê¦M›–,F{»]wu-Zçgz”/ÓH$v²r~IIÑÁŠoÈkÃogl<ÿâHw¾,æÆî-)éC´ëÆ*·Ø'ÊãÖMØ¢4oƒR†tæ&UéSN9¥|è!“òíoû´wÞyç2PLä+**ޝ]»öwY›©œ"… ãû[ŒÏI™@—í›Ö‰¢î®¢Þh—Ù˜´UM4t~Eöº2Ós‡ŒqîÌ Œ,ÈêåYŽÑ†üµÑ½¼tï ^¤›þ!SÆ—Nسõ²m‘Ôçê¦M›–¨‰Å:ZÛÚ2weš°B‚\ f „¶²Üqv_æË$ï\–#¿Ù=娏²s}«¨Lzî8±.›a¤_QÇ¿†Þ±º® CÃÃ@ RV6pÞyçoŸÈ®]»*žx≓ÉdµÊð¬³Îúýƒ>¸ðuGX!éW¼«Ú˜èDv¼°•‰wQ™nÛoA¨ÈF'øuÈÆ+ª×ù@ŠDÉ„;s3#o—(dº÷ðr@EQs—«ãß(&k)²b_4kW n••­Ì=餓óæÌ>üá‡û€R\š†Ê5Uå•Ëöu¹íÒ‹E}ø±aíø¨sª¸ùÇ-ðiy'|Y¡O3?ùÇ!³®?’wŒ…ýyå–E •r >4”ºP«m9ãŒ3ø¼GdêA€~ík_»öرcŸRVVVùÆ7¾ñäßûANtDß~Ŷʯ¬IªHµ¬¡.²S‰kQ=êLÐEÜUþdçC¤Ó„`šÌØ)špÏ^@‚ÑtvùÅä u ™//ïF l¸ÓÛv™}ÖŽpul¹·ÏGØùp*uEáUÑv‘8gíEXçw^ãÖ­[Gœt:šN¥!l3©ú4+ø²­GÓ[x¡H Ûä "u¶ ›Ò’ëƒ0íˆÄ·ÄF%ºórãÎGt\D0¯^7V"ð-*3éŒáÛ*ÒiDýBÀ"  @b8Ô¥pÊÉ'ÿ©¾¾>(ܧ:äú믿ä½÷Þ»RuA*!ĹôÒK_Z³fÍ1Àh;‚ˆËg¡Jœû›DÜE¾uÂ\¶íí‹–TT=D9î )gëÛ× u“s(š˜ %"°pg„º·ÚKd³Í<³B˜}¶œ½‹À¨ð&\™(ê-ÕÀõ§²+8,I¾½*B ëÇ=ÿüóTTTvôõõÍwÝ´ðFLºˆ°jÛÛ7i«ó!²QGáÙù‘¿~MóÛƒ´5õÃþ™ú˯ïï×u! %Ï>ûìýÙ¥ñCn sÇw,ÿÿø[t7[š3gÎ7lØð&àÅÈ"¢T~¢í²r•Xgëe‘l•pæŸM¢¶ª¼zѸDÇ¢«3ó<0Ú^$Œ„;s©—úâ vÙZéÞ›’@~ô;Ï-ŒŠw>ÒͶ÷lMÄ·*z.òÁNt‚Ýø÷ÈëëÒK/íœ^?£±·¯o¾ëRH§Ó`1ë¹—:O]—³.òß ê3Åšþt}™¦Æ¶)N*LýèÒsdǧšœ3°lF†‡¡¯¿\J¡®¶¶é¢‹.jÿ~Èä€ý«¿ú«Ï?ÿüóH$b*ãP(4tà 7ü{öæ@øz#È(üwt1|ÅÎO´]U&Ì¢6"1=ñ®³5™ˆ¨ÎƒÝD¨¯/’E(Ü¡N`4ÍÅìl]® ˆE0påÀ•c+zQYÁm‚îÍ!«WµS vS1ŸëçœOŸó‡æ¦¦Ï§tÔqGÂL »‚ ÍÌ£]æ§@~[’oS'†<›üôÆ–‡8Í…µåÛ@ _O˜ ß–°õÜ8%mXÙ•jDç‰ú󎋎ˆî˯¥ïÙ Î9°ÞÞ^ æÏ›÷ÁòåË»s§"¸×\sÍ^}õÕ›‰DʘBO=õÔmßùÎwvg‹ðË A )Öß…ÎN`ÊÊdúEöìm«Ä¾N4«=¿/Š‚û¯ãdmùñªöóÊewÔEü‘îÙÔ/EÅ»+©·ï=Lfª4ÛŽÍC'PxA)û&aë)³ÏþìC8{")Í6ûÌ׳ý°vü>‹J¸óuîßüÍ×wncÛÁÎãg¦R)ˆ8‡GsÝ…Ñ\FPò6º\nv¿ _m* aNª.Ò'ŠýEÉ•ã{áD@_¯Í‹LPôçÉ‚D" GŽ´“†²²HüÒKW¿UWWçæ:O5ȶmÛªî»ï¾5»wïþK×uùûIÐÐаçG?úÑO³·eÇ×AJK©„;¿¯‹¸ËžME8€úî¤Þç k«Š°ë¢qÊ|ÉŽ'BÙèzFÅ2«´Ø„åáE«è@ԗמßA4¾Îo^E—ùòe¿lÙ²OŸ}öößmÛ¶$í¦íd* áH8¿fŶ©ÂÔ¹/_¡€e› ®Üwc“[UoÒŸ.=I˜b”Ý´m Žwv@|(.u¡®®î£»îºË[?ð&? “ 6|úg?ûÙÚãÇ:èPFõ«_}fõêÕ½€¢AŠMÏO?Â]T.‹j‹¶ƒ wQ?‘uÙDmE>Šõ½T0.Œ¶/âîEU‘e•Ðæ÷E/¾Lè{Ûgk*¸ù¶º1ËŽ u² ƒÈ‡±Ï{î¹ç÷ï¾»ë²þþþSRÉ8"‘ükÛDé)~·µ¶T&¸¾õÂ2ö”â:ÿ´¦í˜õ'jkZoÜ•«à<3Û¶!‘HÀ±£Ç í¤! 'V®\ùúœ9sR€Bn²ã}&º?þøÉ?þñÿ²±±q•ã8U&mÛ¾âŠ+þõ‘Gù°´ÃD?BPe+½*UäY'ØEeºH9_&ÚµS uU½È^´-³aÑé9Ä'£ ‰«Å([/¤|½Hà«„½ŸVdË^Èj:&ÑD…-W ~Ù¤@<Ž<®¼òÊãçœsÎkoüç÷¤Ý4ŒŒŒd„;aD Hn[d£µ•øå#˼r>oœK—Q]´É ö¼|ðَܸ ÆÊÕ³eÞøu~D©1Fç”í#7v¶ždχÇÃàà ̘QÿÁƒnx 0·}Ò³sçÎÊM›6Ííµ×>ûÑG­ŠÇãsLÛBœÏ~ö³?þùç·–rŒò ¡˜¢ÎÔWÐÈ».Ú$êÎnÝçSeDã7©ÙóÇ(k#³CŠ {q*/fÙr‹)7ÞlŽ;0mçïƒ}fÇ%zx>dëÁS-_PxlüñˆöÁ Œ'góÈ#¿~ÓM7×ÞÞ~A"9CÃ!¨ª¬5ÌÁêå_@ÉÖƒ ¾0:,¬ÒFÓ^;¹Øgv@9ž‚6>Æ/ò#<§9A.™ ä¥ÈØÐÓÛÍMMvÓPVVÖwóÍ7=»dɩÀl“rï½÷^õÇ?þqM*•ªñÕgéÒ¥/ìØ±ã9."RH)þNüF’uå~Å»êY%êE"[VçÇFg+*×#¿Œ!B¥”òeþ…‘‰T^ ³6¬ –µ#PøR‹ïSÛ¿h¬²r"/²!Ë—/øò—¿üó'Ÿüñ§’©dõðÐD"(++JižÏ°ñ;†ú@§ |ê£ÿÙza=!ò%Å¢~lçDÖ_P!L¥àãƒaxx€,Y²ä•õë×ïL‘™ XõõõºµÙ YVò¼óÎûÙ¯~õ«ç 󷎯5‚L*]à§Nu={Û²}p—•«|èìùq¨&'ªr?ítuHøå YAËG¥yD6"q.‹jëET¯kÏ— \Ô†ŸD°íT"]w|^ûè£î~õÕ×_ûè£?]ç8i2Ð?ái!…ÂñžsH ·)#™maþº(MbUÐ*O>XÞ8c¯˜(è„4HŽÉï±(s÷©¤^ô@þñúøtww¥Nš9s×£ßùÎ Ñh/H¸W]uÕÛ·o?ǘ4ˆD"+V¬xvëÖ­/—zp‚äúyj"Pev:±®²m{û~nÈ$óË߀ 8™çíE>Tðº¿çJ{Q§É‹éç d:ô;ƒ-{¤z¸ÌCæG´-j'ó#²= Æ÷È#=7cÆô÷(¥L&a`` {GÕL.¸÷œyî‘©µ!v…u£>(ç¸>ù¾ó Š2ú›ÅŒÑâÊ,È?K äólÿ¢íÂ2’7.‹ˆÆï¯Þ²,…lhiiÖÖp] ee‘Þo¼áÿ^tÑE}Þ»ø˜˜‡{ÓM7µOŸ>ýÏ`@MMÍ[n¹åÑ­[·nšcÇ>¦âƒýŽT}‡j¿S>uíLü™ÚðÛü³ªpå Øn›÷%²c÷EíUýˆÚˆöem²Q6‚+Ê o9H êH5»ÍÖS® ðh4oËæ­{oß›U²uì˜øüxÑqðÇÂŽ‹÷Ç׉lùãEõ ŽýÚk¯=ÞÙÙùÝ|ðöõõÇ ®®,+¡O»ª/úTG­umdí3Û£ö:þûS·õ•:cP/;fYõ¶7q± µµ öïߎ“‚P(4tñÅ?ùè£ßÛ“5Ç®)Bmm-œ~úéï·´´\L)Þ¨.÷œ}öÙ›6lذéšk®éLA± ÓÅìǤLT®«¼H‰cþÙT°Ëêek½‹ÊTõ<²ãôûÚàw^ ~!1°H*!*Á:Á®úe}ÈD³ªŽ_e†g'š€ð~d“Ñd@d'¢`Rt÷w´ïß¿ÿ?õ“ŸüÁ“ãq ‹Å ΤÍÉS®^zQeÖÖ5i¤¿ yåʱHÆ`*Ðue–eëºÐ|ø46ÇIeÛβeËžÞ´iÓf@¦"îW\±{Û¶m=©Tªž­°m{pöìÙ;Ö®]û↠>ª®®–}ù!"F¦ ŠõwdêGe§›.²m*ØEe*ñí•óÓ‰€èXT>T¿^x×ÔâA²÷F™EÑlÑë³4vAÚûÙç·Ùcá×yçU´Í‹}Ó2SȺuë–>ýôÓÏ´, ¢Ñ(LŸ>ÊÊÊòY{Ù¶Ê;o[7~‘å¸teõ¹}ϼmÈvÛ¶add„ææfH§Ó`[ÖÈÒ¥K7nß¾}cyy9~PMQ¬E‹}÷øñãË F;êëëÿ¸jÕª-?øÁvÕ××;=F9Á VØtBžŸp˜D¦UâY%æùœwQtÝçëÙ}Õ]Xe¾eõ²1¨ž0]¦H°ÂÀ\´äße5¨0çý©úA=¶ƒ<ƒá¾©IÐ~íÚµ+6¿úê½ñx|>@4…Xm-TUW!$wѪ0 œÙÉ/SD‹Y{‘­È_þòüeÛ˜úÓö§ic46Õ˜ø²ìäÉË»ïëëƒ?ïßÇŽË^›`œ{î¹Oÿê•W~^__ëµOqV¯^}åûï¿ñÉ'Ÿüþ…^¸ëþûïo̾®ø„ ÅC/E?~êüFÝùm•PçŸuÂ]$¦UBÚ±/Õƒm+ó+›ê¸Q´ÍáÔ‰a•°ö#ÄuÂ_ÕFe/{‰t‘ø6ä2;*¨g·ù™¾ òío{áã?úÑïìêº Ù­¬¬„X,g”ˆK‘P¦ªzAÔY&®óê} e¥0fÆDÌ«Žß­î\åìH推CCCÐÜÜ ÍÍÍ022„ˆF£í«V®üç_mÞüZ¶)~PMq:::B###dþüùºœ œRwÖÿ]ÌoËúÖ•‰„¼LÜ«"îì¾LÌ›î›s?íuc‘KÁ6Š÷âÀ w¯L'–u¢@q„¾j_46ð±¯Ú6í*[ÓöB»_ÜT÷Àß¼­©éЗÒét€P8 555‹Å ‰ä.^Í[:R”"x–•©ÚË·i™Î·Ÿú ¶Þ/–eåÎu2™„¶¶68|ø0 @:˲ܺºiïÝxãÚ?þøã{AñC©…{Ð6~¢ðºr“g?x]êŒßˆº‰°õ§:¦¼mîÅA&ÜÌÒStÂ[%úMĹÉd4u2{Þ†·õ¶½èö[¯À÷÷÷[×\sÍ•ï¾ûîÍCÃçxQöH8 µµµPUUåååyË%ÊD¼2"n(nƒâb—ew2/ˆ¨žIgaËò^Tƒã`Åz:†øÐt?ííí¹õÙ)¥‡{/^üÒ÷¿ÿýŸ_vÙe}€ ‚L$¢ïV`TիļnÛ¤L%ÖƒFÝä©2|H¨ËÒtDýðõÂcEÑ^H´-3ñò…³Wç'¯‹Èë"ð¼O¿kÉ£pŸ@XážÛ¹0 o~Ÿ·W }Ó‰n,¢gÞŽ¯“µùà "ÒEeº72;>ºaÆEÏ?ÿüU---_H$'yÂÜ[Ý$Cyy9”••A$H$¡P(A6M?ñöUâZäÇïD@æ;ˆp× {I?®ë‚ëºà¤R0<2###Ç¡¿¿††† ™L‚ãŒ.bYÖÈÌ™3·®X±âÕo~ó›ï/[¶lpýnARâGôñß­A£Id]d/›èÒITv~¢ñ¦‚ÝᆰoÑñä•¡p/žp·AœܳH”—rßÏX@`cR&Ú— ó "Þ¯ÈWú:~ü¸õì³Ïžôì³Ï^xèСUñxüôt:]ÉÞ$ÆÝ죔Êïx÷諒PJsBž­¶m{(‰khhxëâ‹/þÍý÷ßÿáé§Ÿžòê‹2A¤øùŒ6‰º‹êtb]TæW¸›lãì—žh_Ýçë³Cá^dwo[ÙÖ¥ºø‰Èƒm¾LÔ—èXLëARnª‹7âã?ŽüÝßýÝÒ>øàÌŽŽŽ3†††Nv'æ8N•ëºQÙ)Ô¶í˲†lÛî///?RWW÷çÅ‹ïùò—¿üÁwÞy|¢ˆ ‚ " MÚ˜ˆye´YQ®K=Q¥£ðé6~/^•Õ‰|ÉìEãµïÅ‚¸‹"צ‘nÐ {¿ Ϻ2ѾŸh»ªÜ„b„Œ d#ñÛ·o¯Þµk×´¶¶¶ÚžžžÊ¡¡¡òT*ʾ¾ˆBˆ …Ò‘H$‹Åâ K–,é^µjUßYg•€ÑŸ^ñCAdòô³Z—£*²­‹ÈûIQQ]PêçbS?i2¢}ÑØ…{±à…;@¡xæËDuAÄ|PŸ&Ï~ÊDûªr•Ø.vîˆ&ljÈ1úàAAÆ £“RöaZ$uFiçëuÂÞo.bîW°›ŒSÔP¸ ]*ûGË@¶Lµ ÿ‡'Ú–Õ³ðåª?f¶N4vÖÖëCV'«÷Ì,™?G2¿ø ‚|Ò`¿‹¡nb#j£î¢rÙ³·­ò&ý˜ü BVïë5¡”ïcÇ$Z$Êu¢ÝD¼‹ìEÂWõ†QM&X?¼ˆW‰oÞŸÌN'öuoNS!®úcFAO:¦ß{ňÐö&B]dg™×EÛeö¦“]™l¿Ô¿† DÂ]Ý• u€B‘Ì—±¹Âl Œ—«‰hÝD‚µWM$x¿¢ êMĹ â‚ R\tÂÛDlú­u×m넽©¨— x]{L”™Œ¶V¸ë"Ø|½ßè»LxûÁ4F„,Ú¯²åûÕû‰ºcÎ9‚ ‚”“èx1Úðõ*Qob'Ùº_áM"æºr•M`# õ K•‘‰tÕ>€\ÈËf¹¢È>ßÞ/¦¹ï²¾d¤úù(E:Š~AäD¥"p¬"Ô$òΖëD½Jä›îö¥8¿yÚ sÜ‹ƒßu¾UQj™­.²­ºèT–’Ã×ÉÆ¨»èÕô‚W•¨Ø UAäD¡TB¯Xé A½itÜÄÎä?Ñòb—P´^¸ëÒeØ}àõ{Ñ‚¬oÖ—(ÍE&ôAb£³ Sá\LÍ_€ ‚ ' ¢ `1àx w¾Ì϶*ZoM×EóA)%(àÇŠŸˆ{Q/» T–û.Ò*ÑíùQõËd¢a2)懊G©ÊBAÉLQ„£ÿ õ"?û*q.+7í¦ãQÔó‚½8¨V•Ñ•ÉêeÛì¾Làˆß(*ï•«úUWUæÙØ1ZŽ ‚ G)£ô&¢ØD¸óûªh¼iTŸ·Õõå÷<ÛcŽ{qšã®ɲ;Hû‰–³ÑvUSá,šHðu²z•O¿iC‚ ‚ ßÁc‰ÄÀñ¢2•°7ë%Ñ,(ÞÇŽJ¸ËÄ®L¼{¨D´jU]Û^µÂ¨Óœ|¾?AD8FÛAdâ*MÛ5êÎïÈû­3í[5dÑEÜMS`tmDbŸ·—µ0› ˆ„·lb¡zÓªD¶_Ñ^ŠœwAA‚SÌt Qy?i.:•½ ²è|P¿¨wJŒé:î²ra/k#Šxó‘u•æûQ tѱ™ˆlÕ˜Líe`TAA‚Q,ÁXÌ”š ©4A¢á²ö¦i6AA‘>xÂÝ4‚®«W ~U„\U¯»˜Õ$ Çd•LÓjü ;Ï‚ ‚ä# Ëw1lÇš6£Û7Mg1íc‰Þ#ã€éÅ©¼8g1Y_ÕÏR‹ª4“œz•¨– c¿åÅ~CcJ ‚ ‚ø§”Â]էߺ ÑwY™It^%ÒýDâMƆŒ#A.N•ÙùI£‘áGÀÓÖä—ÞW±Áˆ;‚ ‚ø§ÔQb¿AmL…¾i4ݯ`Wµñ;Ù˜"B(¥è£Îª¼pS;Yꊮί­¬?mLý Y*‚ ‚ rJuŸè¼wU[¿bÜÔÖO_¦¾ðFLc„¸ûÉeW!KYÑ­þ¢J‘ÙÊ-'ÉÖÉi/õ›m"~òCA©Šß(p±ú«}HûXÊMü›ˆv?¾‘ÀFÜóÊd¶ Y½[S›REØÇ3ÒQuA) ã-"ý f“v&:ˆMÐ4?6ÊrŒº'¨p}?vA„¼ŸúbµAAdòSÌèúXm‚Fáýú2ñ;Ö‹lQ´‘b wQ¹ßȺ;¿¶*‚Špï‚ òɤâÝ´ßÕ\üŠö “b¤þŠ÷± î¹rU[?é2~ËLëƒÖ•ü‚ RZ&RM ÚÖpöe×ÉwN< ÷àø½sªWL½l%Õ‹"ºSvQæXVZ‘!È ÅÛø†EA©E1#êAÚ•:Õf,ý"ã„q'à?Ú]¬›•êBÒb mŒ#‚ ȉM±ìX¢ò:¿¼”…0âÝSýDÞMËù:Q¹(/ë_„iDÝTã AAJ_ê%"ƒ¶AÆ]Ä=ÏÎÄW‘Ú™´K|¼"è©GAÉÇd[ó=ˆbçÉ›¶S{Œ¶ þLêè¶îMÅŠœ«Pý c¼n¤äoNAA&jyI{?~0?ÁˆRe‚¤ÇÈìx[!/³µ3}ƒŒ‡`Çè:‚ ‚L]ÆCtNô®A}¢`Ÿ$YUÆ« o+²½Ð~}ÊPMŠMUjA™<Œ÷÷x1û+E„¾”~‘€°9îJS_AÇPBûR1YÆ ‚ Hñ™L‚´Ôè›Ö ¶IDAT¼ië»Ìo;&Â]Ú®„öãío²÷‹ ‚ ÈÄ1Q‚³TýŽ[ÊŠõâT¸ ýL@ÛR3™Ç† ‚ ÈÄ1™éXÇV”cCÑ^|r´â=Ïß$ó… ‚ òI¤Xâ¸h"{éŠã" ù@}O“i,‚ ‚Lm&“p-éXP¤/ë8ûb2•ÆŠ ‚ È'—)%tQ˜O.¦Œ bAA¢ƒBúÄæÿpˆ®k@IEND®B`‚pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/transparent.gif0000664000175000017500000000006112203712502031652 0ustar takakitakakiGIF89a‘ÿÿÿÿÿÿ!ù,T;pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/favicon.ico0000664000175000017500000000257612203712502030760 0ustar takakitakakih( " A>;)'%+¯ô=¿ö420520ÿÿÿ;96¸èú „„†µäô—àúGDA/-+LHF+©é\XUtØùååæììì,BO/Vi531WSP+®ñ·çø§§©OÏù--..-.QNK       #!!!!!!"   $$$$$$$$$$$$$$$$À€€Àpyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/pyramid-small.png0000664000175000017500000001560412203712502032114 0ustar takakitakaki‰PNG  IHDRÜ27µsKIDATxœí{˜Õ÷?çTU_æ>0ÃÜp„ŠD(ÞˆšhVØ,."»hbÜUß}³kÖ˜¼‰˜ˆ»„}ˆó¬1’Å Äý0sÙ™°íÛ„ilÛÎe·Yá8yIkkûÅÀÚS=ž!|ú‘3ÂM™2%lãLU6(ÇùDm9®p”"³œÌ(¶Efa.¥’6\Åá†äŒp0,d;#lÁ'òH:J H CÒÞÇ02Iôd]ZŸÂWì(gì€4„Ó¥À?Åèé¿ùŽ]Ü|›àù\ ,g„Êr‚*'¢N*<òxéîŽs×-—pVþFîÌA ÙCx)ï<Òy'2 :GåŸÀ ü°€¿ÆU@!tÞlÞ~ ´°3 pp!ð$ðǶ栟‡"•pïsÀ€CÀ†ösÂÈ¡ ׅ턲xiDF¡®&¨ô]ëìŽ3ãÒÑÌ›y>Öá5Ì»Zò‹W È )"y~z[‚äQá#¢Ð»Ž°Š[ü¨è£^#ð°| !¸ øw7ÿ9àR kí(÷¼B šv¬ 8þŒ–rÛ2Ø §NÇq~#|*±Ÿ¬ãQ)w¨«-áþ;'aÒI¼[ð÷×·°iW˜?n1´A'ÈàyÑÿ ýÜîG?«œyñrJ8”Êê¼H?iDâ´PÐäŸîºˆª2I¬³ åHò’n>ÎKÊ8Ö¦!ídÜ Met‘èã$`#°8­l!pšdÃÝýÕÀ®“Õé§ «€ï¢Uô:©Ÿãî6]ú :d®:êêòø¦'¿”)$†ÔI ‰”Ú¢“‘ØÚÜù7ã˜òÙâm º@YÄ¢ÔU:üãM],Ó41M Ë2±,ËMX@ ‘‚ Ë:Yk8³-"ø/à{n¾˜y2:û”¢X€¶±Þ8µCäÔ†ð—x:ãæ}RÈ'á¢QÅ5Ó«¸ùújâ](倊!”ä‹J®šàÐôyÁò5‚Ád[Bÿç 3‘Ü÷õ§ð ÃýÀKh½8Ï-@pÜÔ—Zc‘T±¼7³ ù ã$£$Unj"Ú©BK\´ûúè[ºý¨´~*j´$Ú Ä2œ[áÖ‰¢m¥Þl2ÿõô瞘À(´—±­9Ä8VUdAÎ$œ‡„ä1MÌDÞ hi¤%Q€` €”cÎ.æ¾[ë0T åDAÅ@Ùhs( *;–Ï-W+.9/€4B„BAB¡¡Pˆp(D(&‡ ‡CÉ”&‘ƒçÓt¸yÏN OëÑ^̪^Ο¼ãÖ½ßW~6Úíý.0ÈC«µ€ßS|uÇ»åÝ:ÞCKáK{éÿónß«Ð/:àhßïÝck/øÎ©E«…ï»ã[öØÞÜK?ån ©d‚Üâö¹ÁmÿOÀ:·}ÅiJºœI¸®®.By…zU—I)¤“ôåµ$Ï’üÃWë(+5ˆEõ‹U @Ù(Bèû.‰c0wÏ<ô\˜Ö0 é“jÚn“R‚ÀuܤØö K7€2ôD8èÝ´£åFwÿ:àçYΟ&Àƒ¾r‰&cMØ»ù¾ã–»À"·ØY£¿®D»Ò_Îи--§cÐ/‘(Žo½à¶µxh@K¿n·Î…À³èøØO3ôp¯³íAÌ„°{î-¾²f÷?ç¶ÿS´$>íCÂ4$á~ÁûÉ%¤&‡ô•Åm˜?³Œ có‰wÛ>w•a¢dGJÀ ¦$5e‚¯]àg¯0M‘ôˆ¦lÜr¡ çÄAõ7héZ"xx¸=碥Fú€ÂèI ðZzˆ¢'tM´Ùh/>FK2ÐoûçÑRu'šð!’®ø à_ÑRèXÚÚÝ­‰žÌÀ{€•nÙW€oºc]„&b-ðÀ¢oý_ÿÏísðŠ;?Iu9a~H’loßGKi&ôCÀh•ô´CNm8SJòóóQ(—\®Äq&š|‚X¦_Ϭ«†aÇHPàDAuC¼:÷ãÄÚô>1Šna0¹\°iT9O¯-¦¤P«–yyyäå… ‡B˜–…ÿc×h,“Ù1 db®DOÆ{Ýý?¯ùŽo~̦¢Õ¾ô7û…Àùnþy’“’vŽ7¡ß¾Œ~ã§ã?2”Å€_£¥ãOÑiÚæôóÛÂh"ÍJ«ó-´:øUà³nÙͤÆ¢_,¢íÇ™À’ cê £ÉZ5þpÔwü5´:º ¸èÛÎ rªR†AAA>Ji/¥!µÄѤ8JRQä֙ŧ 'v køa°£œã`·c·´ â¸î}´àÐmÀ_6ÀËkjùŸwBäA¡`‚‚JKK1b•” î滑Æ¡WIA«rõÀ ´ªÚ9qI—´‡¥è FOÂtÂ} ­;wÐsù‘ßV‰¡%J&²õ…U$%ÞzÎoý:ÃqÐÒùw¬o+2ÔY†–Œ…èɉâ«hõÖF‡Žf¨sMîœ,Õ:Qä”p¦iR\\Œã(M0‰K:‰eš!‰Åº˜÷ùc«Dìû (×ß ´ú®ï$†–!.Ù”Û†°?øë=ÌyìZ"6í´··ÓÜÜLãæF‚ eeeÔÔTŸ,)7½) -ÅîG;ÒñÚ›Xü5ÚéáÅŸBhÂv4fhÛǤª«½¡ý‚¨GK›*’o²>Îým–òÝè—I)Z•Íä´øØ&\_«sÒ"éØÙ‚vdûîXŠz©sJ[ÂY&%%%8¶:îf¹*^KK ï°™[¯ÙM}É¢‘ˆJmL9¨x’pB¸«T\òuÇ¡®|y;w.«G!É€mÛ477³¿¹™x|À„óOª½h±­‚BÛ\¯oõÒÆqô*”ïç‡B¡©kQTT49‰Œ(**ZÞÚÚ I 1}úôð[o½%”R˜¦¹?‹¹7,#„eYÅãñ»Ð^njĒRJÛ¶¥Âoù¯³=ý]$í®löW”¤·6Û8³aI’~HïAñn7vÈéJ+`QZZ‚m;˜¦‰aìÙ³‡õë×óç¶ñÅ©qæ^! €ö8_…a$q¤ vâ K!=Ò è´áÚ±ðw—·ð“Õ•„ƒ*ÕQC2‹FûíBVJy±"ãöÛo/xê©§„ã8äåå½ßÞÞþ÷ÍÍÍp8l{±+ íy3}Ép“ ˜Ë–-;úµ¯}­;‹îCO,9~üøÙo¿ý¶QPPptݺu…hÇJâJ}ôÑ’3f„ººº¨«««B;g2EΜ9órÇqn¡P¨«®®îƒúúú=ÕÕÕÇÚÛÛ+V¬¸!‹'NœØÌQJż¶¾ûÝï6,X°€ë®»nüÊ•+g ÉG«²öO~ò“a÷ÜsiÛ6åååE---gùöOÜ[Ì›ÛòÉ ø–u¹a˜+ ”*GÛAôà ¦% ýÀ-/Ýwß}…¿üå/ó#‘£F Ì®¬¬ôŒÂô‡îßO™LóçÏ<òÈ–ÆÆÆñ;wîœÚÔÔô?Ѧ¦¦K.¸à‚??ÞB«bÞùÂ4Ͱ·HÀ0 m¦;oŒGyäÜ—^zižã8Œ3æ£'Ÿ|rÅå—_ÞŠûzÚ¼y³õ«_ýêºX, ‡Ã^ˆÁ›üƈ#ª½ÆjkkkÐö—w\râĉE–eY¶msî¹çŽF{L=Ò*Àž3g<ð@éáÇ©ªª*ß·oßL·N7ÐýôÓO}å+_1lÛ¦¸¸¸àرcUî±ØÅ_l¾ûî»Þµy÷!NK%ä˜p¡`ˆa¥ÃXµj+_y…ÖcdžI~ždñ½6UÝЭ0Œ6Ž·Ei9;÷±«¥‚ãñQXã(¯¾€Q]€yI€[7òûèÞûGÂ]pV~+£J5 ó `€aÀÿr7óžj ù¸‰e$%›”Û¶©®ªªFÇÃ|2²TÚ˲‚ÂñBÏ18ƒ9kÖ¬w.\8þøñãÅK—.]__ôСC#¤”ö¼yóÞuÛõ·-ðM,¥Å†M†É¶lÙ²KÇ!G–.]úÜ´iÓ"$ #Ž;–ˆþ;Žã¸Ç„r'!ylý¹~”Ti$mÛöëåÞêïå#Ca†!B¡P½J$ÑFCCC¡GÚººº³Ð×(`¯X±Âœ0aB<‰PPP0¹±±qFMMM­¢v¢UÚ. »¾¾~øÖ­[½0ÌiÏ)á"‘O>ù$o®[ç®›4±Å?ÞjpNíqÞú}ˆÍE4·Uc„ÏeDÍxªÎ=ŸË¯:›êš* óSÕþ‹'}ø[:£°¿ùû÷î¢i÷G¼½kÑ)ÛY¸›1åðÀuøÖ‹µØŽp=¤Ú;ª”ðTR…ž$ý ˜ Û¶mw¢cÛ¶·ìi oWçî»ïþð‰'žh>räHåo~ó›q›6m:¨”ÕÕÕ»n¿ýömô\=!DZ=õ, áľ}ûÌŒ6lXË´iÓ§µc´´´Xñx<‰G ¹m½:@@‚éäWJ©LäOyùH)½c¸õ£¾±Û¶ƒi×ãõkÔÕÕ‰ššš=åuË—/¿æ›ßüfIò'^cÆŒ¹rëÖ­ùeeeE<MÌn·Ž§æ:îõæ 9#\Ie%ï½ÿñxÓÔÝÚŽƒ)b<óëc¬Þ0‰Ë®šÅ¤K§ñűc>¬ ßm‡pÎÈ ÎYS?ÜJ¤#ÆÖ­ÛøÓ{ï²|í*>n\K@Dé6ò0¤V-…”H¥PN<}` ”R‰ Õ›„骫«™4iÒV­Zõ¥ÆÆÆqÛ·oï˜:uêz˲¼ñeퟤtM!\ ˆI)€¶¶¶Â––9bÄïÅ gÑ¢E—z„s_)dqI¦Ü>3Îñ% )QJ ¡p'ç¶íÒ»f¯Ž1sæÌ7/rG.^¼xæœ9sÖÖÖúíDV¬X1bíÚµó¼>ƒÁ`:Fè‘-%¹¶j‚€ 2 sF¸cÍÇœ²ò"ýC¬Ž“X¼W»óÙ¾¿‰ ü˜Â¢§(/+㬳΢¡¡††FMMM eeeX–•Ò®mÛ=z„ææìرƒmÛ¶±uë6öíû˜Ã‡ÐÖÞN4'f—0%–¡5Fá~­ ¤ÂQŽ7ÉNáuûí·¿³f͚Ϸµµ•ƒÁ¶¯ýëëIÚBéý'ÊôïÙYY™ª««k:|øpukkkùœ9s¾´lÙ²ÿ.//·×¯__ðï|çêµk×^#„PJ)F ’݃\÷¤]õ6‹´MW¯ÝωS…[&üª+É ¾÷\â .üà /¼°¾©©iòþýûÏ4iÒ?Í;÷…/|á ÛºººÔsÏ=7îå—_¾9‹ ·E"‘Ññx\’$˜§*§ýâñ4¡”b°H7(„Súï„Ð+ Š€â+VŒ¹çÞÿkÚ¶üM€’pPûö£±(‡¦å@ lü3+W¾Œ”’P(Dqq1UU•ÔÖÖRWW‡eYìÚ½›÷îåÀ´¶§;ÚR`¦e%<¡†!±,3é™ô–’I‰£Ç„sǶmÛ°m[2p•€Ù³güàƒnܼyód€úúú÷/»ì²#dVsE<·Çé«çÁ|iÞ¼yãÛÛÛ‡½ñÆ_<ï¼ó&‡Ãá¶£GVÄb±ð¤I“VïÙ³ç쌎D"y¤®»»;q_âñ8î1ÿ˜„mÛŽ7–X,&HµqÇ(¼ûå!eâ»m˜nÞõxu ž}öÙŸÍš5+oß¾}çµ´´4,Y²ä[?þx»ã8†mÛ!!Dlîܹß[·nÝ‘Hdt4õIŽo›ž÷ۜ괖pJ© z`¡›ŠÜF{÷ À>|øp@H©?PH,,Ö;©.{76—táC{G[š¶Ò¸y Ê•Ò00Mýí›aš¾u’Þâe\{M¦Í#œr{`„ …BÝ•••[#‘HiUUÕ>!᫦¦fÏæÍ›'K)ã·ÜrËëøÔ¥ôþóòòºªªª¶tvvTWWïÎÖÿÌ™3÷?öØc>üðÃ7îÝ»÷3‘H¤¬½½½xøðữ¾úêÕÏ>ûìÚ+®¸b¶ã8”””"Õ¶r†1bD ÊËË»ÇS$\QQQguuõ–ŽŽŽ‚ÊÊÊýn”±H)©ªªÚnšfgUU•÷)M¢üüüÎÊÊÊ-]]]ù•••“êåT€š|ôèÑ‘#GŽÜ°k×®GûÓ ô/õÚk¯ kjj*ª¨¨è¸æškƒv>Šh4*ÂápÒzýd:–^Ç0 2Ž¥:*JÛ¶q¯ÇëK‘Û·o7V­Z5¼µµÕ;vì¡n¸á š@ÑX,fwtt8¦iÆòóóÝ·ÄHÆýêeBuM øŸtd•p®Zh‘T Ð’,ä–ëoc4¡l´Qêíû€¨­­mµLëx4Ë—=ˆÖ“@é_ϳ¨õP„Éã25ï'\*Ù¼2½oÑh„â’â ýœè‚‰¬0nºé¦/=zt¤+Ý^ìϘN´ÿk¯½¶åÚk¯=HÚä5 ‚x?Ÿ ŽÊp<= ã'œ¨sÎ9ǹãŽ;"$½Ëžªk[–/..N'U6©Ö#ä3XHN)å-T  UAO%´H®ðts…[!± *‘2®¡¡Á.--iÚßÜ\•bOùåÿb@¥ÌGL™~®¿<-Ÿ$o²=Ó09~¼•˦Oû€Ìމ\@òú믿výúõÐÐа桇úà'×È6É{#\‚tô´ËÒ¥V62§ª¤Ó%šG*oëý>‡§§§_¤G8oëOôüxyãÊ+¯|mÙ²§§ç…ót°8Eí“Y÷¥ï«Dfòô8?퓟Ôvô:ή®Nº»ºwÝ{ï½ïqŠÖÞ½øâ‹åßþö·oÚ´iÓu€(,,ÜõøãÿœT;êLB5“ž¤ó“ÆŸÒ‰–t§DºARjy¤ð_eûŽ¥í MJ?áü«4R¤››â?þñ÷ÒÊ•oµ·µMËÏ×q¶LÒÍS“v˜ÌRž‰¬ú‡aµÓD¦J7Oõt¸hçÎL™2uY]]]¹—&béÒ¥UwÝu×â®®®r€¼¼¼= ,øþ•W^yøŒçtA&ÂyÛLR.pžzÙ›'ÒOºœBø$œ§z‹j yʤJú =É'Ÿyæ™ò{î¹gq8™Ÿ_€B%m-™Ý.“=ˆ—J ÏÉ"S—FL©LISÓ€6mÚ´„S#IÄ®]»¬qãÆý¼³³³fäÈ‘¯/Z´èg³gÏ>“É'N¸tâe’nqúV7=$I™$ÉÖŸ”N¶L¶›Ÿtþ­\¸páÈY´è!Ë4JJK°L+%4 ÓHÒ“\ÉV{ÔK'«ŸhRÒÞÖFSS–e=¿aÆ%………§äMçÝ“ùóçO.--íX²d‰÷·ÇÏ45²¯ëM'ôT ³IºtÂõ©fæ‚p‚äO°ù¥œ‘a¿¿Ž’Þ _}õÕ‚o|ãó[ZZf†B¡a÷ÃÔžÒ)Tž=—‰l’¤C¯ÂhkoçСCD"‘M&LøÅ믿¾zîé‰Â»/gÑ<ôvÝ*-ß›¥/ÒeS7’r°C$œ'á¼”‰\þ2ßçžþ9è•h™>WK–,±lÙ²©üŒmÛ%êáËûuA¢ghÑqœx Ø___¿áG?úÑúóÏ?ßûíÂ3u’ÿoA:á¼mÔËÞV—du¢äJÂyΓtÉ•M}Ìd³ù½’™H–-ÈžéÛ±lèÍHÿmˆhÿ{)<àm{#œ—O'WºÝ–SÂyq¸¾.‚´ã‰•æô­J¦çéGùÎ<ôóçûCºlªf¦ör?ἕÜÞ@=ˆ u¼ûk³ nýÁÉ ]ºÔÊ{Ëiü̓iWú<Òù ç»B´!r áD‘-DàÏŸñ2.§È´–Ò?tÕ1lô²MÏÓò! úç¹ì¯3[꫟AA& çå='ˆ_ÒõF¶L$êXC¤B6œH¨ SY ØŸþN*öÿ½´þÎÆÕ;«t.k~äí!;?ÀÛŸîZ¢Ôq‚–ÜYÁ:ßîw·óuËmMŽ|¬<äÈì¡ñ+3ëÝ9ðípâ-nù³V=F|˜IEND®B`‚pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/ie6.css0000664000175000017500000000136612203712502030030 0ustar takakitakaki* html img, * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none", this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')", this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''), this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')", this.runtimeStyle.backgroundImage = "none")),this.pngSet=true) );} #wrap{display:table;height:100%} pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/static/middlebg.png0000664000175000017500000000535512203712502031112 0ustar takakitakaki‰PNG  IHDR¬º—ƹ CiCCPICC Profilex–wTSYÀï{/½ÐB‘z MJ‘z‘^E%$B °WDWiŠ"‹".¸ºY+¢XX°/È" ¬‹«ˆŠe_ôeÿØý¾³óǜߛ;sïÜ™¹ç<(¾BQ&¬@†H"óñ`ÆÄÆ1ñÝD€ÖpyÙYAáÞ?/3u’±L Ïúuÿ¸Åò a2?›þ¥ÈËKÐBй|A6å<”Ós%Y2û$ÊôÄ4ËÑQV•qò6ÿìó…ÝdÌÏñQYÎYü ¾Œ;PÞ’# Œ¢œŸ#ä¢|eýti†å7(Ó3Ül0™]"ॠl…2EÆAyJò,NœÅÁ24O8™YËÅÂä Ó˜g´vtd3}¹é‰„Âå¥qÅ|&'3#‹+ZÀ—;Ë¢€’¬¶L´ÈöÖŽöö, ´ü_å_¿zý;ÈzûÅãeèçžAŒ®o¶o±ßl™Õ°§ÐÚìøfK, eª÷¾Ùô Ÿ@óY÷aÈæ%E"Ér²´ÌÍ͵ x²‚~•ÿéðÕóŸaÖy²ó¾ÖŽé)HâJÓ%LYQy™é™R13;‹Ë0Ybtëÿ8+­Yy˜‡ ’b=* 2¡(m·ˆ/”3EL¡èŸ:üÃfå Ã/s­æ# /± 7èù½ `hd€ÄïGW ¯} $FÙË‹Öý2÷(£ëŸõß\„~ÂÙÂd¦ÌÌ ‹`ò¤â£oB¦°€ä¨- Œ Øà Ü€ðÁ ĂŀR@ƒ\° ¬ù ì{@9¨5 4€ œÀepÜ}à>#à˜¯Á Axˆ Ñ 5H2€Ì ˆ ͇¼ @( Š… dHI¡UÐF¨*†Ê¡ƒPô#t º]…z »Ð4ý ½ƒ˜ÓaMض„Ù°;GÀ‹àdx)¼΃·Ã¥p5| n†/À×á>x~O!!# Da!l„ƒ#qH"FÖ H R4 mH'r D&·††abXgŒ/&ÃÃ,ŬÁlÔcŽ`š1˜[˜!Ì$æ#–ŠÕÀša°~Øl26›-ÁÖb›°—°}ØìkÇÀáp¾¸X\*n%nn®w׃ÆMáñx5¼ÞŒçâ%ø||þþ¾?‚C ´ 6oBADØ@(!%œ%ôF 3D¢щLä—‹ˆ5Ä6â âq†¤H2"¹"H©¤õ¤RRééé%™LÖ%;’CÉBò:r)ù8ù yˆü–¢D1¥p(ñ)e;å0å<å.å%•J5¤ºQã¨êvjõ"õõMÎBÎOŽ/·V®B®Y®W›¦°©iŠi…é 3ØÌÞLh¶Ï¬Çkîh.2¯6`QXî¬V=kÈ‚ah±Á¢Åâ¹¥¾eœåNËNËVvVéV5V÷­•¬ý­7X·Yÿicjó©°¹=—:×{îÚ¹­s_ØšÙ l÷ÛÞ±£ÙÙm¶k·û`ï`/¶o°wÐwHp¨t`ÓÙ!ìmì+ŽXGÇµŽ§ß:Ù;IœN8ýáÌrNs>ê<6Ïhž`^ͼa]®ËA—ÁùÌù óÌtÕqåºV»>vÓsã»Õºº›¸§ºsîaå!öhò˜æ8qVsÎ{"ž>žžÝ^J^‘^å^¼u½“½ë½'}ì|Vúœ÷ÅúøîôðÓôãùÕùMú;ø¯öï „”<4 ¶ÁAþA»‚,0X ZÐ ‚ý‚w? 1 Yòs(.4$´"ôI˜uت°ÎpZø’ð£á¯#<"Š"îGGJ#Û£ä£â£ê¢¦£=£‹£c,cVÇ\UƶÆáã¢âjã¦z-ܳp$Þ.>?¾‘Ñ¢e‹®.V_œ¾øÌù%Ü%'° Ñ GÞsƒ¹ÕÜ©D¿ÄÊÄI‡·—÷ŒïÆßÍ¸Š£I.IÅIcÉ.É»’ÇS\SJR&„a¹ðEªojUêtZpÚá´OéÑ鄌„ŒS"%Qš¨#S+sYfO–YV~ÖàR§¥{–NŠĵÙPö¢ìV ý™ê’K7I‡ræçTä¼ÉÊ=¹Lq™hY×rÓå[—®ð^ñýJÌJÞÊöU:«Ö¯Zí¾úàhMâšöµzkóÖŽ¬óYwd=i}Úú_6Xm(Þðjcôƶ<ͼuyÛ|6ÕçËå‹ó6;o®Ú‚Ù"ÜÒ½uîÖ²­ ø× ­ K ßoãm»öõw¥ß}Úž´½»È¾hÿÜÑŽþ®;+¯(Þ´«y7swÁîW{–ì¹Zb[Rµ—´Wºw°4°´µL¿lGÙûò”ò¾ ŠÆJÊ­•Óûøûz÷»ío¨Ò¬*¬zw@xàÎAŸƒÍÕ†Õ%‡p‡r=©‰ªéüžý}]­zmaí‡Ã¢ÃƒGÂŽtÔ9ÔÕÕ8ZT×KëÇÅ»ùƒç­ ¬†ƒŒÆÂãà¸ôøÓ~ì?p¢ý$ûdÃO?U6Ñš š¡æåÍ“-)-ƒ­±­=§üOµ·9·5ýlñóáÓ:§+Î(Ÿ):K:›wöÓ¹ç¦ÎgŸ¸|a¸}Iûý‹1ow„vt_ ¸tå²÷å‹î箸\9}Õéê©kìk-×í¯7wÙu5ýb÷KS·}wó ‡­7o¶õÌë9ÛëÚ{á–ç­Ë·ýn_ï[Ð×ÓÙg ~`ðÿÎØÝô»/îåÜ›¹¿îöAÁC…‡%4Uÿjòkã ýà™!Ï¡®Çáïó†Ÿý–ýÛû‘¼'Ô'%£Ú£uc6c§Ç½Ço>]øtäYÖ³™‰ü߯|nüü§?Üþ蚌™y!~ñéÏm/Õ^~eûª}*dêÑëŒ×3ÓoÔÞyË~Ûù.úÝèLî{üûÒ&Ú>||ð)ãÓ§¿›óüìÎçŠ pHYs  šœPIDAT(cøøñã& €ÿÿÿ‡²H#SØ4½ø¹8]E„¶A¢ÍTô¶àÄ&†ÍDˆaSB¬ë±9§%Hr3NÏ $,Â&…¨½´›IEND®B`‚pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/.badfile0000664000175000017500000000000012203712502026713 0ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/0000775000175000017500000000000012210157153027337 5ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl0000664000175000017500000000661612210154276033135 0ustar takakitakaki The Pyramid Web Application Development Framework
pyramid

Welcome to ${project}, an application generated by
the Pyramid web application development framework.

pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/resources.py0000664000175000017500000000012412203712502027717 0ustar takakitakakiclass Root(object): def __init__(self, request): self.request = request pyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/test_no_content.py_tmpl0000664000175000017500000000000012203712502032137 0ustar takakitakakipyramid-1.4.5/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl0000664000175000017500000000146112203712502027676 0ustar takakitakaki[app:main] use = egg:{{project}} pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.debug_templates = true pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar [server:main] use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 # Begin logging configuration [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_{{package_logger}}] level = DEBUG handlers = qualname = {{package}} [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s # End logging configuration pyramid-1.4.5/pyramid/tests/test_scaffolds/test_copydir.py0000664000175000017500000003675312210154276023374 0ustar takakitakaki# -*- coding: utf-8 -*- import unittest import os import pkg_resources class Test_copy_dir(unittest.TestCase): def setUp(self): import tempfile from pyramid.compat import NativeIO self.dirname = tempfile.mkdtemp() self.out = NativeIO() self.fixturetuple = ('pyramid.tests.test_scaffolds', 'fixture_scaffold') def tearDown(self): import shutil shutil.rmtree(self.dirname, ignore_errors=True) self.out.close() def _callFUT(self, *arg, **kw): kw['out_'] = self.out from pyramid.scaffolds.copydir import copy_dir return copy_dir(*arg, **kw) def test_copy_source_as_pkg_resource(self): vars = {'package':'mypackage'} self._callFUT(self.fixturetuple, self.dirname, vars, 1, False, template_renderer=dummy_template_renderer) result = self.out.getvalue() self.assertTrue('Creating' in result) self.assertTrue( 'Copying fixture_scaffold/+package+/__init__.py_tmpl to' in result) source = pkg_resources.resource_filename( 'pyramid.tests.test_scaffolds', 'fixture_scaffold/+package+/__init__.py_tmpl') target = os.path.join(self.dirname, 'mypackage', '__init__.py') with open(target, 'r') as f: tcontent = f.read() with open(source, 'r') as f: scontent = f.read() self.assertEqual(scontent, tcontent) def test_copy_source_as_dirname(self): vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) self._callFUT(source, self.dirname, vars, 1, False, template_renderer=dummy_template_renderer) result = self.out.getvalue() self.assertTrue('Creating' in result) self.assertTrue('Copying __init__.py_tmpl to' in result) source = pkg_resources.resource_filename( 'pyramid.tests.test_scaffolds', 'fixture_scaffold/+package+/__init__.py_tmpl') target = os.path.join(self.dirname, 'mypackage', '__init__.py') with open(target, 'r') as f: tcontent = f.read() with open(source, 'r') as f: scontent = f.read() self.assertEqual(scontent, tcontent) def test_content_is_same_message(self): vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) self._callFUT(source, self.dirname, vars, 2, False, template_renderer=dummy_template_renderer) self._callFUT(source, self.dirname, vars, 2, False, template_renderer=dummy_template_renderer) result = self.out.getvalue() self.assertTrue('%s already exists (same content)' % \ os.path.join(self.dirname, 'mypackage', '__init__.py') in result) def test_direxists_message(self): vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) # if not os.path.exists(self.dirname): # os.mkdir(self.dirname) self._callFUT(source, self.dirname, vars, 2, False, template_renderer=dummy_template_renderer) result = self.out.getvalue() self.assertTrue('Directory %s exists' % self.dirname in result, result) def test_overwrite_false(self): vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) self._callFUT(source, self.dirname, vars, 1, False, overwrite=False, template_renderer=dummy_template_renderer) target = os.path.join(self.dirname, 'mypackage', '__init__.py') with open(target, 'w') as f: f.write('These are not the words you are looking for.') self._callFUT(source, self.dirname, vars, 1, False, overwrite=False, template_renderer=dummy_template_renderer) def test_detect_SkipTemplate(self): vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) def dummy_template_renderer(*args, **kwargs): from pyramid.scaffolds.copydir import SkipTemplate raise SkipTemplate self._callFUT(source, self.dirname, vars, 1, False, template_renderer=dummy_template_renderer) def test_query_interactive(self): from pyramid.scaffolds import copydir vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) self._callFUT(source, self.dirname, vars, 1, False, overwrite=False, template_renderer=dummy_template_renderer) target = os.path.join(self.dirname, 'mypackage', '__init__.py') with open(target, 'w') as f: f.write('These are not the words you are looking for.') # We need query_interactive to return False in order to force # execution of a branch original_code_object = copydir.query_interactive copydir.query_interactive = lambda *args, **kwargs: False self._callFUT(source, self.dirname, vars, 1, False, interactive=True, overwrite=False, template_renderer=dummy_template_renderer) copydir.query_interactive = original_code_object class Test_raise_SkipTemplate(unittest.TestCase): def _callFUT(self, *arg, **kw): from pyramid.scaffolds.copydir import skip_template return skip_template(*arg, **kw) def test_raise_SkipTemplate(self): from pyramid.scaffolds.copydir import SkipTemplate self.assertRaises(SkipTemplate, self._callFUT, True, "exc-message") class Test_makedirs(unittest.TestCase): def _callFUT(self, *arg, **kw): from pyramid.scaffolds.copydir import makedirs return makedirs(*arg, **kw) def test_makedirs_parent_dir(self): import shutil import tempfile tmpdir = tempfile.mkdtemp() target = os.path.join(tmpdir, 'nonexistent_subdir') self._callFUT(target, 2, None) shutil.rmtree(tmpdir) def test_makedirs_no_parent_dir(self): import shutil import tempfile tmpdir = tempfile.mkdtemp() target = os.path.join(tmpdir, 'nonexistent_subdir', 'non2') self._callFUT(target, 2, None) shutil.rmtree(tmpdir) class Test_support_functions(unittest.TestCase): def _call_html_quote(self, *arg, **kw): from pyramid.scaffolds.copydir import html_quote return html_quote(*arg, **kw) def _call_url_quote(self, *arg, **kw): from pyramid.scaffolds.copydir import url_quote return url_quote(*arg, **kw) def _call_test(self, *arg, **kw): from pyramid.scaffolds.copydir import test return test(*arg, **kw) def test_html_quote(self): import string s = None self.assertEqual(self._call_html_quote(s), '') s = string.ascii_letters self.assertEqual(self._call_html_quote(s), s) s = "Λεμεσός" self.assertEqual(self._call_url_quote(s), "%CE%9B%CE%B5%CE%BC%CE%B5%CF%83%CF%8C%CF%82") def test_url_quote(self): import string s = None self.assertEqual(self._call_url_quote(s), '') s = string.ascii_letters self.assertEqual(self._call_url_quote(s), s) s = "Λεμεσός" self.assertEqual(self._call_url_quote(s), "%CE%9B%CE%B5%CE%BC%CE%B5%CF%83%CF%8C%CF%82") def test_test(self): conf = True true_cond = "faked" self.assertEqual(self._call_test( conf, true_cond, false_cond=None), "faked") conf = False self.assertEqual(self._call_test( conf, true_cond, false_cond="alsofaked"), "alsofaked") class Test_should_skip_file(unittest.TestCase): def _callFUT(self, *arg, **kw): from pyramid.scaffolds.copydir import should_skip_file return should_skip_file(*arg, **kw) def test_should_skip_dot_hidden_file(self): self.assertEqual( self._callFUT('.a_filename'), 'Skipping hidden file %(filename)s') def test_should_skip_backup_file(self): for name in ('a_filename~', 'a_filename.bak'): self.assertEqual( self._callFUT(name), 'Skipping backup file %(filename)s') def test_should_skip_bytecompiled_file(self): for name in ('afilename.pyc', 'afilename.pyo'): extension = os.path.splitext(name)[1] self.assertEqual( self._callFUT(name), 'Skipping %s file ' % extension + '%(filename)s') def test_should_skip_jython_class_file(self): self.assertEqual( self._callFUT('afilename$py.class'), 'Skipping $py.class file %(filename)s') def test_should_skip_version_control_directory(self): for name in ('CVS', '_darcs'): self.assertEqual( self._callFUT(name), 'Skipping version control directory %(filename)s') def test_valid_file_is_not_skipped(self): self.assertEqual( self._callFUT('a_filename'), None) class RawInputMockObject( object ): count = 0 def __init__( self, fake_input ): self.input= fake_input self.count = 0 def __call__( self, prompt ): # Don't cycle endlessly. self.count += 1 if self.count > 1: return 'y' else: return self.input class Test_query_interactive(unittest.TestCase): def setUp(self): import tempfile from pyramid.compat import NativeIO self.dirname = tempfile.mkdtemp() self.out = NativeIO() self.fixturetuple = ('pyramid.tests.test_scaffolds', 'fixture_scaffold') self.src_content = """\ These are not the droids that you are looking for.""" self.dest_content = """\ These are the droids for whom you are looking; now you have found them.""" self.src_fn = os.path.join(self.dirname, 'mypackage', '__init__.py') self.dest_fn = os.path.join(self.dirname, 'mypackage', '__init__.py') # query_interactive is only normally executed when the destination # is discovered to be already occupied by existing files, so ... # create the required occupancy. from pyramid.scaffolds.copydir import copy_dir copy_dir(self.fixturetuple, self.dirname, {'package':'mypackage'}, 0, False, template_renderer=dummy_template_renderer) def tearDown(self): import shutil shutil.rmtree(self.dirname, ignore_errors=True) self.out.close() def _callFUT(self, *arg, **kw): from pyramid.scaffolds.copydir import query_interactive return query_interactive(*arg, **kw) def test_query_interactive_0y(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("y") self._callFUT(self.src_fn, self.dest_fn, self.src_content, self.dest_content, simulate=False, out_=self.out) self.assertTrue("Replace" in self.out.getvalue()) def test_query_interactive_1n(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("n") self._callFUT(self.src_fn, self.dest_fn, self.src_content, '\n'.join(self.dest_content.split('\n')[:-1]), simulate=False, out_=self.out) self.assertTrue("Replace" in self.out.getvalue()) def test_query_interactive_2b(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("b") with open(os.path.join( self.dirname, 'mypackage', '__init__.py.bak'), 'w') as fp: fp.write("") fp.close() self._callFUT(self.src_fn, self.dest_fn, self.dest_content, self.src_content, simulate=False, out_=self.out) self.assertTrue("Backing up" in self.out.getvalue()) def test_query_interactive_3d(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("d") self._callFUT(self.src_fn, self.dest_fn, self.dest_content, self.src_content, simulate=False, out_=self.out) output = self.out.getvalue() # The useful text in self.out gets wiped out on the second # call to raw_input, otherwise the test could be made # more usefully precise... # print("3d", output) # self.assertTrue("@@" in output, output) self.assertTrue("Replace" in output) def test_query_interactive_4dc(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("dc") self._callFUT(self.src_fn, self.dest_fn, self.dest_content, self.src_content, simulate=False, out_=self.out) output = self.out.getvalue() # The useful text in self.out gets wiped out on the second # call to raw_input, otherwise, the test could be made # more usefully precise... # print("4dc", output) # self.assertTrue("***" in output, output) self.assertTrue("Replace" in output) def test_query_interactive_5allbad(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("all z") self._callFUT(self.src_fn, self.dest_fn, self.src_content, self.dest_content, simulate=False, out_=self.out) output = self.out.getvalue() # The useful text in self.out gets wiped out on the second # call to raw_input, otherwise the test could be made # more usefully precise... # print("5allbad", output) # self.assertTrue("Responses" in output, output) self.assertTrue("Replace" in output) def test_query_interactive_6all(self): from pyramid.scaffolds import copydir copydir.input_ = RawInputMockObject("all b") self._callFUT(self.src_fn, self.dest_fn, self.src_content, self.dest_content, simulate=False, out_=self.out) output = self.out.getvalue() # The useful text in self.out gets wiped out on the second # call to raw_input, otherwise the test could be made # more usefully precise... # print("6all", output) # self.assertTrue("Responses" in output, output) self.assertTrue("Replace" in output) def dummy_template_renderer(content, v, filename=None): return content pyramid-1.4.5/pyramid/tests/test_scaffolds/__init__.py0000664000175000017500000000001212203712502022370 0ustar takakitakaki# package pyramid-1.4.5/pyramid/tests/test_scaffolds/test_template.py0000664000175000017500000001146312210154276023525 0ustar takakitakakiimport unittest from pyramid.compat import bytes_ class TestTemplate(unittest.TestCase): def _makeOne(self, name='whatever'): from pyramid.scaffolds.template import Template return Template(name) def test_render_template_success(self): inst = self._makeOne() result = inst.render_template('{{a}} {{b}}', {'a':'1', 'b':'2'}) self.assertEqual(result, bytes_('1 2')) def test_render_template_expr_failure(self): inst = self._makeOne() self.assertRaises(AttributeError, inst.render_template, '{{a.foo}}', {'a':'1', 'b':'2'}) def test_render_template_expr_success(self): inst = self._makeOne() result = inst.render_template('{{a.lower()}}', {'a':'A'}) self.assertEqual(result, b'a') def test_render_template_expr_success_via_pipe(self): inst = self._makeOne() result = inst.render_template('{{b|c|a.lower()}}', {'a':'A'}) self.assertEqual(result, b'a') def test_render_template_expr_success_via_pipe2(self): inst = self._makeOne() result = inst.render_template('{{b|a.lower()|c}}', {'a':'A'}) self.assertEqual(result, b'a') def test_render_template_expr_value_is_None(self): inst = self._makeOne() result = inst.render_template('{{a}}', {'a':None}) self.assertEqual(result, b'') def test_module_dir(self): import sys import pkg_resources package = sys.modules['pyramid.scaffolds.template'] path = pkg_resources.resource_filename(package.__name__, '') inst = self._makeOne() result = inst.module_dir() self.assertEqual(result, path) def test_template_dir__template_dir_is_None(self): inst = self._makeOne() self.assertRaises(AssertionError, inst.template_dir) def test_template_dir__template_dir_is_tuple(self): inst = self._makeOne() inst._template_dir = ('a', 'b') self.assertEqual(inst.template_dir(), ('a', 'b')) def test_template_dir__template_dir_is_not_None(self): import os import sys import pkg_resources package = sys.modules['pyramid.scaffolds.template'] path = pkg_resources.resource_filename(package.__name__, '') inst = self._makeOne() inst._template_dir ='foo' result = inst.template_dir() self.assertEqual(result, os.path.join(path, 'foo')) def test_write_files_path_exists(self): import os import sys import pkg_resources package = sys.modules['pyramid.scaffolds.template'] path = pkg_resources.resource_filename(package.__name__, '') inst = self._makeOne() inst._template_dir = 'foo' inst.exists = lambda *arg: True copydir = DummyCopydir() inst.copydir = copydir command = DummyCommand() inst.write_files(command, 'output dir', {'a':1}) self.assertEqual(copydir.template_dir, os.path.join(path, 'foo')) self.assertEqual(copydir.output_dir, 'output dir') self.assertEqual(copydir.vars, {'a':1}) self.assertEqual(copydir.kw, {'template_renderer':inst.render_template, 'indent':1, 'verbosity':1, 'simulate':False, 'overwrite':False, 'interactive':False, }) def test_write_files_path_missing(self): L = [] inst = self._makeOne() inst._template_dir = 'foo' inst.exists = lambda *arg: False inst.out = lambda *arg: None inst.makedirs = lambda dir: L.append(dir) copydir = DummyCopydir() inst.copydir = copydir command = DummyCommand() inst.write_files(command, 'output dir', {'a':1}) self.assertEqual(L, ['output dir']) def test_run(self): L = [] inst = self._makeOne() inst._template_dir = 'foo' inst.exists = lambda *arg: False inst.out = lambda *arg: None inst.makedirs = lambda dir: L.append(dir) copydir = DummyCopydir() inst.copydir = copydir command = DummyCommand() inst.run(command, 'output dir', {'a':1}) self.assertEqual(L, ['output dir']) def test_check_vars(self): inst = self._makeOne() self.assertRaises(RuntimeError, inst.check_vars, 'one', 'two') class DummyCopydir(object): def copy_dir(self, template_dir, output_dir, vars, **kw): self.template_dir = template_dir self.output_dir = output_dir self.vars = vars self.kw = kw class DummyOptions(object): simulate = False overwrite = False interactive = False class DummyCommand(object): options = DummyOptions() verbosity = 1 pyramid-1.4.5/pyramid/tests/test_wsgi.py0000664000175000017500000001106012203712502017643 0ustar takakitakakiimport unittest class WSGIAppTests(unittest.TestCase): def _callFUT(self, app): from pyramid.wsgi import wsgiapp return wsgiapp(app) def test_decorator(self): context = DummyContext() request = DummyRequest() decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) def test_decorator_object_instance(self): context = DummyContext() request = DummyRequest() app = DummyApp() decorator = self._callFUT(app) response = decorator(context, request) self.assertEqual(response, app) class WSGIApp2Tests(unittest.TestCase): def _callFUT(self, app): from pyramid.wsgi import wsgiapp2 return wsgiapp2(app) def test_decorator_with_subpath_and_view_name(self): context = DummyContext() request = DummyRequest() request.subpath = ('subpath',) request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/b/view_name/subpath'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) self.assertEqual(request.environ['PATH_INFO'], '/subpath') self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/b/view_name') def test_decorator_with_subpath_no_view_name(self): context = DummyContext() request = DummyRequest() request.subpath = ('subpath',) request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/b/subpath'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) self.assertEqual(request.environ['PATH_INFO'], '/subpath') self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/b') def test_decorator_no_subpath_with_view_name(self): context = DummyContext() request = DummyRequest() request.subpath = () request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/b/view_name'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) self.assertEqual(request.environ['PATH_INFO'], '/') self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/b/view_name') def test_decorator_traversed_empty_with_view_name(self): context = DummyContext() request = DummyRequest() request.subpath = () request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/view_name'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) self.assertEqual(request.environ['PATH_INFO'], '/') self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/view_name') def test_decorator_traversed_empty_no_view_name(self): context = DummyContext() request = DummyRequest() request.subpath = () request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) self.assertEqual(request.environ['PATH_INFO'], '/') self.assertEqual(request.environ['SCRIPT_NAME'], '/foo') def test_decorator_traversed_empty_no_view_name_no_script_name(self): context = DummyContext() request = DummyRequest() request.subpath = () request.environ = {'SCRIPT_NAME':'', 'PATH_INFO':'/'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) self.assertEqual(request.environ['PATH_INFO'], '/') self.assertEqual(request.environ['SCRIPT_NAME'], '') def test_decorator_on_callable_object_instance(self): context = DummyContext() request = DummyRequest() request.subpath = () request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/'} app = DummyApp() decorator = self._callFUT(app) response = decorator(context, request) self.assertEqual(response, app) self.assertEqual(request.environ['PATH_INFO'], '/') self.assertEqual(request.environ['SCRIPT_NAME'], '/foo') def dummyapp(environ, start_response): """ """ class DummyApp(object): def __call__(self, environ, start_response): """ """ class DummyContext: pass class DummyRequest: def get_response(self, application): return application def copy(self): self.copied = True return self pyramid-1.4.5/pyramid/tests/test_mako_templating.py0000664000175000017500000005704712210154301022057 0ustar takakitakaki## come on python gimme some of that sweet, sweet -*- coding: utf-8 -*- import shutil import tempfile import unittest from pyramid import testing from pyramid.compat import ( text_, text_type, ) class Base(object): def setUp(self): self.config = testing.setUp() self.config.begin() import os here = os.path.abspath(os.path.dirname(__file__)) self.templates_dir = os.path.join(here, 'fixtures') def tearDown(self): self.config.end() def maybe_unittest(): # The latest release of MarkupSafe (0.17) which is used by Mako is # incompatible with Python 3.2, so we skip these tests if we cannot # import a Mako module which ends up importing MarkupSafe. Note that # this version of MarkupSafe *is* compatible with Python 2.6, 2.7, and 3.3, # so these tests should not be skipped on those platforms. try: import mako.lookup except (ImportError, SyntaxError, AttributeError): # pragma: no cover return object else: return unittest.TestCase class Test_renderer_factory(Base, maybe_unittest()): def _callFUT(self, info): from pyramid.mako_templating import renderer_factory return renderer_factory(info) def _getLookup(self, name='mako.'): from pyramid.mako_templating import IMakoLookup return self.config.registry.getUtility(IMakoLookup, name=name) def test_hyphen_filenames(self): from pyramid.mako_templating import renderer_factory info = DummyRendererInfo({ 'name':'app:moon-and-world.mak', 'package':None, 'registry':self.config.registry, 'settings':{}, 'type': '' }) result = renderer_factory(info) self.assertEqual(result.path, 'app:moon-and-world.mak') def test_no_directories(self): info = DummyRendererInfo({ 'name':'pyramid.tests:fixtures/helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':{}, }) renderer = self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.directories, []) self.assertEqual(lookup.filesystem_checks, False) self.assertEqual(renderer.path, 'pyramid.tests:fixtures/helloworld.mak') self.assertEqual(renderer.lookup, lookup) def test_no_lookup(self): settings = {'mako.directories':self.templates_dir} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) renderer = self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.directories, [self.templates_dir]) self.assertEqual(lookup.filesystem_checks, False) self.assertEqual(renderer.path, 'helloworld.mak') self.assertEqual(renderer.lookup, lookup) def test_composite_directories_path(self): twice = '\n' + self.templates_dir + '\n' + self.templates_dir + '\n' settings = {'mako.directories':twice} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.directories, [self.templates_dir]*2) def test_directories_list(self): import sys import os.path settings = {'mako.directories':['a', 'b']} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() module_path = os.path.dirname( sys.modules['__main__'].__file__).rstrip('.') # ./setup.py self.assertEqual(lookup.directories, [ os.path.join(module_path, 'a'), os.path.join(module_path, 'b')]) def test_with_module_directory_asset_spec(self): import os module_directory = 'pyramid.tests:fixtures' settings = {'mako.directories':self.templates_dir, 'mako.module_directory':module_directory} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() fixtures = os.path.join(os.path.dirname(__file__), 'fixtures') self.assertEqual(lookup.module_directory, fixtures) def test_with_module_directory_asset_abspath(self): import os fixtures = os.path.join(os.path.dirname(__file__), 'fixtures') settings = {'mako.directories':self.templates_dir, 'mako.module_directory':fixtures} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.module_directory, fixtures) def test_with_input_encoding(self): settings = {'mako.directories':self.templates_dir, 'mako.input_encoding':'utf-16'} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['input_encoding'], 'utf-16') def test_with_error_handler(self): settings = {'mako.directories':self.templates_dir, 'mako.error_handler':'pyramid.tests'} import pyramid.tests info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['error_handler'], pyramid.tests) def test_with_preprocessor(self): settings = {'mako.directories':self.templates_dir, 'mako.preprocessor':'pyramid.tests'} import pyramid.tests info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['preprocessor'], pyramid.tests) def test_with_default_filters(self): settings = {'mako.directories':self.templates_dir, 'mako.default_filters':'\nh\ng\n\n'} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['default_filters'], ['h', 'g']) def test_with_default_filters_list(self): settings = {'mako.directories':self.templates_dir, 'mako.default_filters':['h', 'g']} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['default_filters'], ['h', 'g']) def test_with_imports(self): settings = {'mako.directories':self.templates_dir, 'mako.imports':'\none\ntwo\n\n'} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['imports'], ['one', 'two']) def test_with_imports_list(self): settings = {'mako.directories':self.templates_dir, 'mako.imports':['one', 'two']} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['imports'], ['one', 'two']) def test_with_strict_undefined_true(self): settings = {'mako.directories':self.templates_dir, 'mako.strict_undefined':'true'} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['strict_undefined'], True) def test_with_strict_undefined_false(self): settings = {'mako.directories':self.templates_dir, 'mako.strict_undefined':'false'} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, }) self._callFUT(info) lookup = self._getLookup() self.assertEqual(lookup.template_args['strict_undefined'], False) def test_with_lookup(self): from pyramid.mako_templating import IMakoLookup lookup = dict() self.config.registry.registerUtility(lookup, IMakoLookup, name='mako.') info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':{}, }) renderer = self._callFUT(info) self.assertEqual(renderer.lookup, lookup) self.assertEqual(renderer.path, 'helloworld.mak') def test_space_dot_name(self): from pyramid.mako_templating import renderer_factory info = DummyRendererInfo({ 'name':'hello .world.mako', 'package':None, 'registry':self.config.registry, 'settings':{}, }) result = renderer_factory(info) self.assertEqual(result.path, 'hello .world.mako') self.assertTrue(result.defname is None) def test_space_dot_name_def(self): from pyramid.mako_templating import renderer_factory info = DummyRendererInfo({ 'name':'hello .world#comp.mako', 'package':None, 'registry':self.config.registry, 'settings':{}, }) result = renderer_factory(info) self.assertEqual(result.path, 'hello .world.mako') self.assertEqual(result.defname, 'comp') class MakoRendererFactoryHelperTests(Base, maybe_unittest()): def _getTargetClass(self): from pyramid.mako_templating import MakoRendererFactoryHelper return MakoRendererFactoryHelper def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def _getLookup(self, name='mako.'): from pyramid.mako_templating import IMakoLookup return self.config.registry.getUtility(IMakoLookup, name=name) def test_no_settings_prefix(self): settings = {'foo.directories':self.templates_dir} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, 'type':'foo', }) helper = self._makeOne() renderer = helper(info) lookup = self._getLookup('foo.') self.assertEqual(lookup.directories, [self.templates_dir]) self.assertEqual(lookup.filesystem_checks, False) self.assertEqual(renderer.path, 'helloworld.mak') self.assertEqual(renderer.lookup, lookup) def test_custom_settings_prefix(self): settings = {'bar.directories':self.templates_dir} info = DummyRendererInfo({ 'name':'helloworld.mak', 'package':None, 'registry':self.config.registry, 'settings':settings, 'type':'foo', }) helper = self._makeOne('bar.') renderer = helper(info) lookup = self._getLookup('bar.') self.assertEqual(lookup.directories, [self.templates_dir]) self.assertEqual(lookup.filesystem_checks, False) self.assertEqual(renderer.path, 'helloworld.mak') self.assertEqual(renderer.lookup, lookup) class MakoLookupTemplateRendererTests(Base, maybe_unittest()): def _getTargetClass(self): from pyramid.mako_templating import MakoLookupTemplateRenderer return MakoLookupTemplateRenderer def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) def test_instance_implements_ITemplate(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ITemplateRenderer verifyObject(ITemplateRenderer, self._makeOne(None, None, None)) def test_class_implements_ITemplate(self): from zope.interface.verify import verifyClass from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) def test_call(self): lookup = DummyLookup() instance = self._makeOne('path', None, lookup) result = instance({}, {'system':1}) self.assertTrue(isinstance(result, text_type)) self.assertEqual(result, text_('result')) def test_call_with_system_context(self): # lame lookup = DummyLookup() instance = self._makeOne('path', None, lookup) result = instance({}, {'context':1}) self.assertTrue(isinstance(result, text_type)) self.assertEqual(result, text_('result')) self.assertEqual(lookup.values, {'_context':1}) def test_call_with_tuple_value(self): lookup = DummyLookup() instance = self._makeOne('path', None, lookup) result = instance(('fub', {}), {'context':1}) self.assertEqual(lookup.deffed, 'fub') self.assertEqual(result, text_('result')) self.assertEqual(lookup.values, {'_context':1}) def test_call_with_defname(self): lookup = DummyLookup() instance = self._makeOne('path', 'defname', lookup) result = instance({}, {'system':1}) self.assertTrue(isinstance(result, text_type)) self.assertEqual(result, text_('result')) def test_call_with_defname_with_tuple_value(self): lookup = DummyLookup() instance = self._makeOne('path', 'defname', lookup) result = instance(('defname', {}), {'context':1}) self.assertEqual(lookup.deffed, 'defname') self.assertEqual(result, text_('result')) self.assertEqual(lookup.values, {'_context':1}) def test_call_with_defname_with_tuple_value_twice(self): lookup = DummyLookup() instance1 = self._makeOne('path', 'defname', lookup) result1 = instance1(('defname1', {}), {'context':1}) self.assertEqual(lookup.deffed, 'defname1') self.assertEqual(result1, text_('result')) self.assertEqual(lookup.values, {'_context':1}) instance2 = self._makeOne('path', 'defname', lookup) result2 = instance2(('defname2', {}), {'context':2}) self.assertNotEqual(lookup.deffed, 'defname1') self.assertEqual(lookup.deffed, 'defname2') self.assertEqual(result2, text_('result')) self.assertEqual(lookup.values, {'_context':2}) def test_call_with_nondict_value(self): lookup = DummyLookup() instance = self._makeOne('path', None, lookup) self.assertRaises(ValueError, instance, None, {}) def test_call_render_raises(self): from pyramid.mako_templating import MakoRenderingException lookup = DummyLookup(exc=NotImplementedError) instance = self._makeOne('path', None, lookup) try: instance({}, {}) except MakoRenderingException as e: self.assertTrue('NotImplementedError' in e.text) else: # pragma: no cover raise AssertionError def test_implementation(self): lookup = DummyLookup() instance = self._makeOne('path', None, lookup) result = instance.implementation().render_unicode() self.assertTrue(isinstance(result, text_type)) self.assertEqual(result, text_('result')) class TestIntegration(maybe_unittest()): def setUp(self): import pyramid.mako_templating self.config = testing.setUp() self.config.add_settings({'mako.directories': 'pyramid.tests:fixtures'}) self.config.add_renderer('.mak', pyramid.mako_templating.renderer_factory) def tearDown(self): self.config.end() def test_render(self): from pyramid.renderers import render result = render('helloworld.mak', {'a':1}).replace('\r','') self.assertEqual(result, text_('\nHello föö\n', 'utf-8')) def test_render_from_fs(self): from pyramid.renderers import render self.config.add_settings({'reload_templates': True}) result = render('helloworld.mak', {'a':1}).replace('\r','') self.assertEqual(result, text_('\nHello föö\n', 'utf-8')) def test_render_inheritance(self): from pyramid.renderers import render result = render('helloinherit.mak', {}).replace('\r','') self.assertEqual(result, text_('Layout\nHello World!\n')) def test_render_inheritance_pkg_spec(self): from pyramid.renderers import render result = render('hello_inherit_pkg.mak', {}).replace('\r','') self.assertEqual(result, text_('Layout\nHello World!\n')) def test_render_namespace(self): from pyramid.renderers import render result = render('hellocompo.mak', {}).replace('\r','') self.assertEqual(result, text_('\nNamespace\nHello \nWorld!\n')) def test_render_to_response(self): from pyramid.renderers import render_to_response result = render_to_response('helloworld.mak', {'a':1}) self.assertEqual(result.ubody.replace('\r',''), text_('\nHello föö\n', 'utf-8')) def test_render_to_response_pkg_spec(self): from pyramid.renderers import render_to_response result = render_to_response('pyramid.tests:fixtures/helloworld.mak', {'a':1}) self.assertEqual(result.ubody.replace('\r', ''), text_('\nHello föö\n', 'utf-8')) def test_render_with_abs_path(self): from pyramid.renderers import render result = render('/helloworld.mak', {'a':1}).replace('\r','') self.assertEqual(result, text_('\nHello föö\n', 'utf-8')) def test_get_renderer(self): from pyramid.renderers import get_renderer result = get_renderer('helloworld.mak') self.assertEqual( result.implementation().render_unicode().replace('\r',''), text_('\nHello föö\n', 'utf-8')) def test_template_not_found(self): from pyramid.renderers import render from mako.exceptions import TemplateLookupException self.assertRaises(TemplateLookupException, render, 'helloworld_not_here.mak', {}) def test_template_default_escaping(self): from pyramid.renderers import render result = render('nonminimal.mak', {'name':'fred'}).replace('\r','') self.assertEqual(result, text_('Hello, <b>fred</b>!\n')) class TestPkgResourceTemplateLookup(maybe_unittest()): def _makeOne(self, **kw): from pyramid.mako_templating import PkgResourceTemplateLookup return PkgResourceTemplateLookup(**kw) def get_fixturedir(self): import os import pyramid.tests return os.path.join(os.path.dirname(pyramid.tests.__file__), 'fixtures') def test_adjust_uri_not_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('a', None) self.assertEqual(result, '/a') def test_adjust_uri_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('a:b', None) self.assertEqual(result, 'a:b') def test_adjust_uri_asset_spec_with_modified_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('a$b', None) self.assertEqual(result, 'a:b') def test_adjust_uri_not_asset_spec_with_relativeto_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('c', 'a:b') self.assertEqual(result, 'a:c') def test_adjust_uri_not_asset_spec_with_relativeto_modified_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('c', 'a$b') self.assertEqual(result, 'a:c') def test_adjust_uri_not_asset_spec_with_relativeto_not_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('b', '../a') self.assertEqual(result, '../b') def test_adjust_uri_not_asset_spec_abs_with_relativeto_asset_spec(self): inst = self._makeOne() result = inst.adjust_uri('/c', 'a:b') self.assertEqual(result, '/c') def test_adjust_uri_asset_spec_with_relativeto_not_asset_spec_abs(self): inst = self._makeOne() result = inst.adjust_uri('a:b', '/c') self.assertEqual(result, 'a:b') def test_get_template_not_asset_spec(self): fixturedir = self.get_fixturedir() inst = self._makeOne(directories=[fixturedir]) result = inst.get_template('helloworld.mak') self.assertFalse(result is None) def test_get_template_asset_spec_with_filesystem_checks(self): inst = self._makeOne(filesystem_checks=True) result = inst.get_template('pyramid.tests:fixtures/helloworld.mak') self.assertFalse(result is None) def test_get_template_asset_spec_with_module_dir(self): tmpdir = tempfile.mkdtemp() try: inst = self._makeOne(module_directory=tmpdir) result = inst.get_template('pyramid.tests:fixtures/helloworld.mak') self.assertFalse(result is None) finally: shutil.rmtree(tmpdir, ignore_errors=True) def test_get_template_asset_spec_missing(self): from mako.exceptions import TopLevelLookupException fixturedir = self.get_fixturedir() inst = self._makeOne(filesystem_checks=True, directories=[fixturedir]) self.assertRaises(TopLevelLookupException, inst.get_template, 'pyramid.tests:fixtures/notthere.mak') class TestMakoRenderingException(unittest.TestCase): def _makeOne(self, text): from pyramid.mako_templating import MakoRenderingException return MakoRenderingException(text) def test_repr_and_str(self): exc = self._makeOne('text') self.assertEqual(str(exc), 'text') self.assertEqual(repr(exc), 'text') class DummyLookup(object): def __init__(self, exc=None): self.exc = exc def get_template(self, path): self.path = path return self def get_def(self, path): self.deffed = path return self def render_unicode(self, **values): if self.exc: raise self.exc self.values = values return text_('result') class DummyRendererInfo(object): def __init__(self, kw): self.__dict__.update(kw) pyramid-1.4.5/pyramid/tests/test_static.py0000664000175000017500000003603012203712502020165 0ustar takakitakakiimport datetime import unittest # 5 years from now (more or less) fiveyrsfuture = datetime.datetime.utcnow() + datetime.timedelta(5*365) class Test_static_view_use_subpath_False(unittest.TestCase): def _getTargetClass(self): from pyramid.static import static_view return static_view def _makeOne(self, *arg, **kw): return self._getTargetClass()(*arg, **kw) def _makeRequest(self, kw=None): from pyramid.request import Request environ = { 'wsgi.url_scheme':'http', 'wsgi.version':(1,0), 'SERVER_NAME':'example.com', 'SERVER_PORT':'6543', 'PATH_INFO':'/', 'SCRIPT_NAME':'', 'REQUEST_METHOD':'GET', } if kw is not None: environ.update(kw) return Request(environ=environ) def test_ctor_defaultargs(self): inst = self._makeOne('package:resource_name') self.assertEqual(inst.package_name, 'package') self.assertEqual(inst.docroot, 'resource_name') self.assertEqual(inst.cache_max_age, 3600) self.assertEqual(inst.index, 'index.html') def test_call_adds_slash_path_info_empty(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':''}) context = DummyContext() from pyramid.httpexceptions import HTTPMovedPermanently self.assertRaises(HTTPMovedPermanently, inst, context, request) def test_path_info_slash_means_index_html(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) def test_oob_singledot(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/./index.html'}) context = DummyContext() response = inst(context, request) self.assertEqual(response.status, '200 OK') self.assertTrue(b'static' in response.body) def test_oob_emptyelement(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'//index.html'}) context = DummyContext() response = inst(context, request) self.assertEqual(response.status, '200 OK') self.assertTrue(b'static' in response.body) def test_oob_dotdotslash(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/subdir/../../minimal.pt'}) context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_oob_dotdotslash_encoded(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest( {'PATH_INFO':'/subdir/%2E%2E%2F%2E%2E/minimal.pt'}) context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_oob_os_sep(self): import os inst = self._makeOne('pyramid.tests:fixtures/static') dds = '..' + os.sep request = self._makeRequest({'PATH_INFO':'/subdir/%s%sminimal.pt' % (dds, dds)}) context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_resource_doesnt_exist(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/notthere'}) context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_resource_isdir(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/subdir/'}) context = DummyContext() response = inst(context, request) self.assertTrue(b'subdir' in response.body) def test_resource_is_file(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/index.html'}) context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) def test_resource_is_file_with_wsgi_file_wrapper(self): from pyramid.response import _BLOCK_SIZE inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/index.html'}) class _Wrapper(object): def __init__(self, file, block_size=None): self.file = file self.block_size = block_size request.environ['wsgi.file_wrapper'] = _Wrapper context = DummyContext() response = inst(context, request) app_iter = response.app_iter self.assertTrue(isinstance(app_iter, _Wrapper)) self.assertTrue(b'static' in app_iter.file.read()) self.assertEqual(app_iter.block_size, _BLOCK_SIZE) app_iter.file.close() def test_resource_is_file_with_cache_max_age(self): inst = self._makeOne('pyramid.tests:fixtures/static', cache_max_age=600) request = self._makeRequest({'PATH_INFO':'/index.html'}) context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) self.assertEqual(len(response.headerlist), 5) header_names = [ x[0] for x in response.headerlist ] header_names.sort() self.assertEqual(header_names, ['Cache-Control', 'Content-Length', 'Content-Type', 'Expires', 'Last-Modified']) def test_resource_is_file_with_no_cache_max_age(self): inst = self._makeOne('pyramid.tests:fixtures/static', cache_max_age=None) request = self._makeRequest({'PATH_INFO':'/index.html'}) context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) self.assertEqual(len(response.headerlist), 3) header_names = [ x[0] for x in response.headerlist ] header_names.sort() self.assertEqual( header_names, ['Content-Length', 'Content-Type', 'Last-Modified']) def test_resource_notmodified(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/index.html'}) request.if_modified_since = fiveyrsfuture context = DummyContext() response = inst(context, request) start_response = DummyStartResponse() app_iter = response(request.environ, start_response) try: self.assertEqual(start_response.status, '304 Not Modified') self.assertEqual(list(app_iter), []) finally: app_iter.close() def test_not_found(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/notthere.html'}) context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_resource_with_content_encoding(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/arcs.svg.tgz'}) context = DummyContext() response = inst(context, request) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'application/x-tar') self.assertEqual(response.content_encoding, 'gzip') response.app_iter.close() def test_resource_no_content_encoding(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':'/index.html'}) context = DummyContext() response = inst(context, request) self.assertEqual(response.status, '200 OK') self.assertEqual(response.content_type, 'text/html') self.assertEqual(response.content_encoding, None) response.app_iter.close() class Test_static_view_use_subpath_True(unittest.TestCase): def _getTargetClass(self): from pyramid.static import static_view return static_view def _makeOne(self, *arg, **kw): kw['use_subpath'] = True return self._getTargetClass()(*arg, **kw) def _makeRequest(self, kw=None): from pyramid.request import Request environ = { 'wsgi.url_scheme':'http', 'wsgi.version':(1,0), 'SERVER_NAME':'example.com', 'SERVER_PORT':'6543', 'PATH_INFO':'/', 'SCRIPT_NAME':'', 'REQUEST_METHOD':'GET', } if kw is not None: environ.update(kw) return Request(environ=environ) def test_ctor_defaultargs(self): inst = self._makeOne('package:resource_name') self.assertEqual(inst.package_name, 'package') self.assertEqual(inst.docroot, 'resource_name') self.assertEqual(inst.cache_max_age, 3600) self.assertEqual(inst.index, 'index.html') def test_call_adds_slash_path_info_empty(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest({'PATH_INFO':''}) request.subpath = () context = DummyContext() from pyramid.httpexceptions import HTTPMovedPermanently self.assertRaises(HTTPMovedPermanently, inst, context, request) def test_path_info_slash_means_index_html(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = () context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) def test_oob_singledot(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('.', 'index.html') context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_oob_emptyelement(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('', 'index.html') context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_oob_dotdotslash(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('subdir', '..', '..', 'minimal.pt') context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_oob_dotdotslash_encoded(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('subdir', '%2E%2E', '%2E%2E', 'minimal.pt') context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_oob_os_sep(self): import os inst = self._makeOne('pyramid.tests:fixtures/static') dds = '..' + os.sep request = self._makeRequest() request.subpath = ('subdir', dds, dds, 'minimal.pt') context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_resource_doesnt_exist(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('notthere,') context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) def test_resource_isdir(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('subdir',) context = DummyContext() response = inst(context, request) self.assertTrue(b'subdir' in response.body) def test_resource_is_file(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('index.html',) context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) def test_resource_is_file_with_cache_max_age(self): inst = self._makeOne('pyramid.tests:fixtures/static', cache_max_age=600) request = self._makeRequest() request.subpath = ('index.html',) context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) self.assertEqual(len(response.headerlist), 5) header_names = [ x[0] for x in response.headerlist ] header_names.sort() self.assertEqual(header_names, ['Cache-Control', 'Content-Length', 'Content-Type', 'Expires', 'Last-Modified']) def test_resource_is_file_with_no_cache_max_age(self): inst = self._makeOne('pyramid.tests:fixtures/static', cache_max_age=None) request = self._makeRequest() request.subpath = ('index.html',) context = DummyContext() response = inst(context, request) self.assertTrue(b'static' in response.body) self.assertEqual(len(response.headerlist), 3) header_names = [ x[0] for x in response.headerlist ] header_names.sort() self.assertEqual( header_names, ['Content-Length', 'Content-Type', 'Last-Modified']) def test_resource_notmodified(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.if_modified_since = fiveyrsfuture request.subpath = ('index.html',) context = DummyContext() response = inst(context, request) start_response = DummyStartResponse() app_iter = response(request.environ, start_response) try: self.assertEqual(start_response.status, '304 Not Modified') self.assertEqual(list(app_iter), []) finally: app_iter.close() def test_not_found(self): inst = self._makeOne('pyramid.tests:fixtures/static') request = self._makeRequest() request.subpath = ('notthere.html',) context = DummyContext() from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) class DummyContext: pass class DummyStartResponse: status = () headers = () def __call__(self, status, headers): self.status = status self.headers = headers pyramid-1.4.5/pyramid/tests/test_url.py0000664000175000017500000011721012210154276017506 0ustar takakitakakiimport os import unittest import warnings from pyramid import testing from pyramid.compat import ( text_, native_, WIN, ) class TestURLMethodsMixin(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _makeOne(self, environ=None): from pyramid.url import URLMethodsMixin if environ is None: environ = {} class Request(URLMethodsMixin): application_url = 'http://example.com:5432' script_name = '' def __init__(self, environ): self.environ = environ request = Request(environ) request.registry = self.config.registry return request def _registerContextURL(self, reg): with warnings.catch_warnings(record=True): from pyramid.interfaces import IContextURL from zope.interface import Interface class DummyContextURL(object): def __init__(self, context, request): pass def __call__(self): return 'http://example.com/context/' reg.registerAdapter(DummyContextURL, (Interface, Interface), IContextURL) def _registerResourceURL(self, reg): 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/' reg.registerAdapter(DummyResourceURL, (Interface, Interface), IResourceURL) def test_resource_url_root_default(self): request = self._makeOne() self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_url(root) self.assertEqual(result, 'http://example.com:5432/context/') def test_resource_url_extra_args(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, 'this/theotherthing', 'that') self.assertEqual( result, 'http://example.com:5432/context/this%2Ftheotherthing/that') def test_resource_url_unicode_in_element_names(self): request = self._makeOne() self._registerResourceURL(request.registry) uc = text_(b'La Pe\xc3\xb1a', 'utf-8') context = DummyContext() result = request.resource_url(context, uc) self.assertEqual(result, 'http://example.com:5432/context/La%20Pe%C3%B1a') def test_resource_url_at_sign_in_element_names(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, '@@myview') self.assertEqual(result, 'http://example.com:5432/context/@@myview') def test_resource_url_element_names_url_quoted(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, 'a b c') self.assertEqual(result, 'http://example.com:5432/context/a%20b%20c') def test_resource_url_with_query_dict(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() uc = text_(b'La Pe\xc3\xb1a', 'utf-8') result = request.resource_url(context, 'a', query={'a':uc}) self.assertEqual(result, 'http://example.com:5432/context/a?a=La+Pe%C3%B1a') def test_resource_url_with_query_seq(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() uc = text_(b'La Pe\xc3\xb1a', 'utf-8') result = request.resource_url(context, 'a', query=[('a', 'hi there'), ('b', uc)]) self.assertEqual(result, 'http://example.com:5432/context/a?a=hi+there&b=La+Pe%C3%B1a') def test_resource_url_with_query_empty(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, 'a', query=[]) self.assertEqual(result, 'http://example.com:5432/context/a') def test_resource_url_anchor_is_after_root_when_no_elements(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, anchor='a') self.assertEqual(result, 'http://example.com:5432/context/#a') def test_resource_url_anchor_is_after_elements_when_no_qs(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, 'a', anchor='b') self.assertEqual(result, 'http://example.com:5432/context/a#b') def test_resource_url_anchor_is_after_qs_when_qs_is_present(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, 'a', query={'b':'c'}, anchor='d') self.assertEqual(result, 'http://example.com:5432/context/a?b=c#d') def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() uc = text_(b'La Pe\xc3\xb1a', 'utf-8') result = request.resource_url(context, anchor=uc) self.assertEqual( result, native_( text_(b'http://example.com:5432/context/#La Pe\xc3\xb1a', 'utf-8'), 'utf-8') ) def test_resource_url_anchor_is_not_urlencoded(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() result = request.resource_url(context, anchor=' /#') self.assertEqual(result, 'http://example.com:5432/context/# /#') def test_resource_url_no_IResourceURL_registered(self): # falls back to ResourceURL root = DummyContext() root.__name__ = '' root.__parent__ = None request = self._makeOne() request.environ = {} result = request.resource_url(root) self.assertEqual(result, 'http://example.com:5432/') def test_resource_url_no_registry_on_request(self): request = self._makeOne() self._registerResourceURL(request.registry) del request.registry root = DummyContext() result = request.resource_url(root) self.assertEqual(result, 'http://example.com:5432/context/') def test_resource_url_finds_IContextURL(self): request = self._makeOne() self._registerContextURL(request.registry) root = DummyContext() with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') result = request.resource_url(root) self.assertEqual(len(w), 1) self.assertEqual(result, 'http://example.com/context/') def test_resource_url_with_app_url(self): request = self._makeOne() self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_url(root, app_url='http://somewhere.com') self.assertEqual(result, 'http://somewhere.com/context/') def test_resource_url_with_scheme(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'8080', 'SERVER_NAME':'example.com', } request = self._makeOne(environ) self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_url(root, scheme='https') self.assertEqual(result, 'https://example.com/context/') def test_resource_url_with_host(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'8080', 'SERVER_NAME':'example.com', } request = self._makeOne(environ) self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_url(root, host='someotherhost.com') self.assertEqual(result, 'http://someotherhost.com:8080/context/') def test_resource_url_with_port(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'8080', 'SERVER_NAME':'example.com', } request = self._makeOne(environ) self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_url(root, port='8181') self.assertEqual(result, 'http://example.com:8181/context/') def test_resource_url_with_local_url(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'8080', 'SERVER_NAME':'example.com', } request = self._makeOne(environ) self._registerResourceURL(request.registry) root = DummyContext() def resource_url(req, info): self.assertEqual(req, request) self.assertEqual(info['virtual_path'], '/context/') self.assertEqual(info['physical_path'], '/context/') self.assertEqual(info['app_url'], 'http://example.com:5432') return 'http://example.com/contextabc/' root.__resource_url__ = resource_url result = request.resource_url(root) self.assertEqual(result, 'http://example.com/contextabc/') def test_resource_path(self): request = self._makeOne() self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_path(root) self.assertEqual(result, '/context/') def test_resource_path_kwarg(self): request = self._makeOne() self._registerResourceURL(request.registry) root = DummyContext() result = request.resource_path(root, anchor='abc') self.assertEqual(result, '/context/#abc') def test_route_url_with_elements(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', 'extra1', 'extra2') self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2') def test_route_url_with_elements_path_endswith_slash(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3/')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', 'extra1', 'extra2') self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2') def test_route_url_no_elements(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', a=1, b=2, c=3, _query={'a':1}, _anchor=text_(b"foo")) self.assertEqual(result, 'http://example.com:5432/1/2/3?a=1#foo') def test_route_url_with_anchor_binary(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _anchor=b"La Pe\xc3\xb1a") self.assertEqual( result, native_( text_( b'http://example.com:5432/1/2/3#La Pe\xc3\xb1a', 'utf-8'), 'utf-8') ) def test_route_url_with_anchor_unicode(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) anchor = text_(b'La Pe\xc3\xb1a', 'utf-8') result = request.route_url('flub', _anchor=anchor) self.assertEqual( result, native_( text_( b'http://example.com:5432/1/2/3#La Pe\xc3\xb1a', 'utf-8'), 'utf-8') ) def test_route_url_with_query(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _query={'q':'1'}) self.assertEqual(result, 'http://example.com:5432/1/2/3?q=1') def test_route_url_with_empty_query(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _query={}) self.assertEqual(result, 'http://example.com:5432/1/2/3') def test_route_url_with_app_url(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _app_url='http://example2.com') self.assertEqual(result, 'http://example2.com/1/2/3') def test_route_url_with_host(self): from pyramid.interfaces import IRoutesMapper environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'5432', } request = self._makeOne(environ) mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _host='someotherhost.com') self.assertEqual(result, 'http://someotherhost.com:5432/1/2/3') def test_route_url_with_port(self): from pyramid.interfaces import IRoutesMapper environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'5432', 'SERVER_NAME':'example.com', } request = self._makeOne(environ) mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _port='8080') self.assertEqual(result, 'http://example.com:8080/1/2/3') def test_route_url_with_scheme(self): from pyramid.interfaces import IRoutesMapper environ = { 'wsgi.url_scheme':'http', 'SERVER_PORT':'5432', 'SERVER_NAME':'example.com', } request = self._makeOne(environ) mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _scheme='https') self.assertEqual(result, 'https://example.com/1/2/3') def test_route_url_generation_error(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(raise_exc=KeyError) request.registry.registerUtility(mapper, IRoutesMapper) mapper.raise_exc = KeyError self.assertRaises(KeyError, request.route_url, 'flub', request, a=1) def test_route_url_generate_doesnt_receive_query_or_anchor(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() route = DummyRoute(result='') mapper = DummyRoutesMapper(route=route) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _query=dict(name='some_name')) self.assertEqual(route.kw, {}) # shouldnt have anchor/query self.assertEqual(result, 'http://example.com:5432?name=some_name') def test_route_url_with_pregenerator(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() route = DummyRoute(result='/1/2/3') def pregenerator(request, elements, kw): return ('a',), {'_app_url':'http://example2.com'} route.pregenerator = pregenerator mapper = DummyRoutesMapper(route=route) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub') self.assertEqual(result, 'http://example2.com/1/2/3/a') self.assertEqual(route.kw, {}) # shouldnt have anchor/query def test_route_url_with_anchor_app_url_elements_and_query(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute(result='/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', 'element1', _app_url='http://example2.com', _anchor='anchor', _query={'q':'1'}) self.assertEqual(result, 'http://example2.com/1/2/3/element1?q=1#anchor') def test_route_url_integration_with_real_request(self): # to try to replicate https://github.com/Pylons/pyramid/issues/213 from pyramid.interfaces import IRoutesMapper from pyramid.request import Request request = Request.blank('/') request.registry = self.config.registry mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', 'extra1', 'extra2') self.assertEqual(result, 'http://localhost/1/2/3/extra1/extra2') def test_current_route_url_current_request_has_no_route(self): request = self._makeOne() self.assertRaises(ValueError, request.current_route_url) def test_current_route_url_with_elements_query_and_anchor(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() route = DummyRoute('/1/2/3') mapper = DummyRoutesMapper(route=route) request.matched_route = route request.matchdict = {} request.registry.registerUtility(mapper, IRoutesMapper) result = request.current_route_url('extra1', 'extra2', _query={'a':1}, _anchor=text_(b"foo")) self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') def test_current_route_url_with_route_name(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() route = DummyRoute('/1/2/3') mapper = DummyRoutesMapper(route=route) request.matched_route = route request.matchdict = {} request.registry.registerUtility(mapper, IRoutesMapper) result = request.current_route_url('extra1', 'extra2', _query={'a':1}, _anchor=text_(b"foo"), _route_name='bar') self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') def test_current_route_path(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() route = DummyRoute('/1/2/3') mapper = DummyRoutesMapper(route=route) request.matched_route = route request.matchdict = {} request.script_name = '/script_name' request.registry.registerUtility(mapper, IRoutesMapper) result = request.current_route_path('extra1', 'extra2', _query={'a':1}, _anchor=text_(b"foo")) self.assertEqual(result, '/script_name/1/2/3/extra1/extra2?a=1#foo') def test_route_path_with_elements(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) request.script_name = '' result = request.route_path('flub', 'extra1', 'extra2', a=1, b=2, c=3, _query={'a':1}, _anchor=text_(b"foo")) self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo') def test_route_path_with_script_name(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() request.script_name = '/foo' mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_path('flub', 'extra1', 'extra2', a=1, b=2, c=3, _query={'a':1}, _anchor=text_(b"foo")) self.assertEqual(result, '/foo/1/2/3/extra1/extra2?a=1#foo') def test_static_url_staticurlinfo_notfound(self): request = self._makeOne() self.assertRaises(ValueError, request.static_url, 'static/foo.css') def test_static_url_abspath(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() info = DummyStaticURLInfo('abc') registry = request.registry registry.registerUtility(info, IStaticURLInfo) abspath = makeabs('static', 'foo.css') result = request.static_url(abspath) self.assertEqual(result, 'abc') self.assertEqual(info.args, (makeabs('static', 'foo.css'), request, {})) request = self._makeOne() def test_static_url_found_rel(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() info = DummyStaticURLInfo('abc') request.registry.registerUtility(info, IStaticURLInfo) result = request.static_url('static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {}) ) def test_static_url_abs(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() info = DummyStaticURLInfo('abc') request.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_static_url_found_abs_no_registry_on_request(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() registry = request.registry info = DummyStaticURLInfo('abc') registry.registerUtility(info, IStaticURLInfo) del request.registry 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_static_url_abspath_integration_with_staticurlinfo(self): import os from pyramid.interfaces import IStaticURLInfo from pyramid.config.views import StaticURLInfo info = StaticURLInfo() here = os.path.abspath(os.path.dirname(__file__)) info.add(self.config, 'absstatic', here) request = self._makeOne() registry = request.registry registry.registerUtility(info, IStaticURLInfo) abspath = os.path.join(here, 'test_url.py') result = request.static_url(abspath) self.assertEqual(result, 'http://example.com:5432/absstatic/test_url.py') def test_static_path_abspath(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() request.script_name = '/foo' info = DummyStaticURLInfo('abc') registry = request.registry registry.registerUtility(info, IStaticURLInfo) abspath = makeabs('static', 'foo.css') result = request.static_path(abspath) self.assertEqual(result, 'abc') self.assertEqual(info.args, (makeabs('static', 'foo.css'), request, {'_app_url':'/foo'}) ) def test_static_path_found_rel(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() request.script_name = '/foo' info = DummyStaticURLInfo('abc') request.registry.registerUtility(info, IStaticURLInfo) result = request.static_path('static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {'_app_url':'/foo'}) ) def test_static_path_abs(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() request.script_name = '/foo' info = DummyStaticURLInfo('abc') request.registry.registerUtility(info, IStaticURLInfo) result = request.static_path('pyramid.tests:static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {'_app_url':'/foo'}) ) def test_static_path(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() request.script_name = '/foo' info = DummyStaticURLInfo('abc') request.registry.registerUtility(info, IStaticURLInfo) result = request.static_path('static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {'_app_url':'/foo'}) ) def test_partial_application_url_with_http_host_default_port_http(self): environ = { 'wsgi.url_scheme':'http', 'HTTP_HOST':'example.com:80', } request = self._makeOne(environ) result = request._partial_application_url() self.assertEqual(result, 'http://example.com') def test_partial_application_url_with_http_host_default_port_https(self): environ = { 'wsgi.url_scheme':'https', 'HTTP_HOST':'example.com:443', } request = self._makeOne(environ) result = request._partial_application_url() self.assertEqual(result, 'https://example.com') def test_partial_application_url_with_http_host_nondefault_port_http(self): environ = { 'wsgi.url_scheme':'http', 'HTTP_HOST':'example.com:8080', } request = self._makeOne(environ) result = request._partial_application_url() self.assertEqual(result, 'http://example.com:8080') def test_partial_application_url_with_http_host_nondefault_port_https(self): environ = { 'wsgi.url_scheme':'https', 'HTTP_HOST':'example.com:4443', } request = self._makeOne(environ) result = request._partial_application_url() self.assertEqual(result, 'https://example.com:4443') def test_partial_application_url_with_http_host_no_colon(self): environ = { 'wsgi.url_scheme':'http', 'HTTP_HOST':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url() self.assertEqual(result, 'http://example.com') def test_partial_application_url_no_http_host(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url() self.assertEqual(result, 'http://example.com') def test_partial_application_replace_port(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url(port=8080) self.assertEqual(result, 'http://example.com:8080') def test_partial_application_replace_scheme_https_special_case(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url(scheme='https') self.assertEqual(result, 'https://example.com') def test_partial_application_replace_scheme_https_special_case_avoid(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url(scheme='https', port='8080') self.assertEqual(result, 'https://example.com:8080') def test_partial_application_replace_scheme_http_special_case(self): environ = { 'wsgi.url_scheme':'https', 'SERVER_NAME':'example.com', 'SERVER_PORT':'8080', } request = self._makeOne(environ) result = request._partial_application_url(scheme='http') self.assertEqual(result, 'http://example.com') def test_partial_application_replace_scheme_http_special_case_avoid(self): environ = { 'wsgi.url_scheme':'https', 'SERVER_NAME':'example.com', 'SERVER_PORT':'8000', } request = self._makeOne(environ) result = request._partial_application_url(scheme='http', port='8080') self.assertEqual(result, 'http://example.com:8080') def test_partial_application_replace_host_no_port(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url(host='someotherhost.com') self.assertEqual(result, 'http://someotherhost.com') def test_partial_application_replace_host_with_port(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'8000', } request = self._makeOne(environ) result = request._partial_application_url(host='someotherhost.com:8080') self.assertEqual(result, 'http://someotherhost.com:8080') def test_partial_application_replace_host_and_port(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url(host='someotherhost.com:8080', port='8000') self.assertEqual(result, 'http://someotherhost.com:8000') def test_partial_application_replace_host_port_and_scheme(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'80', } request = self._makeOne(environ) result = request._partial_application_url( host='someotherhost.com:8080', port='8000', scheme='https', ) self.assertEqual(result, 'https://someotherhost.com:8000') def test_partial_application_url_with_custom_script_name(self): environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'example.com', 'SERVER_PORT':'8000', } request = self._makeOne(environ) request.script_name = '/abc' result = request._partial_application_url() self.assertEqual(result, 'http://example.com:8000/abc') class Test_route_url(unittest.TestCase): def _callFUT(self, route_name, request, *elements, **kw): from pyramid.url import route_url return route_url(route_name, request, *elements, **kw) def _makeRequest(self): class Request(object): def route_url(self, route_name, *elements, **kw): self.route_name = route_name self.elements = elements self.kw = kw return 'route url' return Request() def test_it(self): request = self._makeRequest() result = self._callFUT('abc', request, 'a', _app_url='') self.assertEqual(result, 'route url') self.assertEqual(request.route_name, 'abc') self.assertEqual(request.elements, ('a',)) self.assertEqual(request.kw, {'_app_url':''}) class Test_route_path(unittest.TestCase): def _callFUT(self, route_name, request, *elements, **kw): from pyramid.url import route_path return route_path(route_name, request, *elements, **kw) def _makeRequest(self): class Request(object): def route_path(self, route_name, *elements, **kw): self.route_name = route_name self.elements = elements self.kw = kw return 'route path' return Request() def test_it(self): request = self._makeRequest() result = self._callFUT('abc', request, 'a', _app_url='') self.assertEqual(result, 'route path') self.assertEqual(request.route_name, 'abc') self.assertEqual(request.elements, ('a',)) self.assertEqual(request.kw, {'_app_url':''}) class Test_resource_url(unittest.TestCase): def _callFUT(self, resource, request, *elements, **kw): from pyramid.url import resource_url return resource_url(resource, request, *elements, **kw) def _makeRequest(self): class Request(object): def resource_url(self, resource, *elements, **kw): self.resource = resource self.elements = elements self.kw = kw return 'resource url' return Request() def test_it(self): request = self._makeRequest() result = self._callFUT('abc', request, 'a', _app_url='') self.assertEqual(result, 'resource url') self.assertEqual(request.resource, 'abc') self.assertEqual(request.elements, ('a',)) self.assertEqual(request.kw, {'_app_url':''}) class Test_static_url(unittest.TestCase): def _callFUT(self, path, request, **kw): from pyramid.url import static_url return static_url(path, request, **kw) def _makeRequest(self): class Request(object): def static_url(self, path, **kw): self.path = path self.kw = kw return 'static url' return Request() def test_it_abs(self): request = self._makeRequest() result = self._callFUT('/foo/bar/abc', request, _app_url='') self.assertEqual(result, 'static url') self.assertEqual(request.path, '/foo/bar/abc') self.assertEqual(request.kw, {'_app_url':''}) def test_it_absspec(self): request = self._makeRequest() result = self._callFUT('foo:abc', request, _anchor='anchor') self.assertEqual(result, 'static url') self.assertEqual(request.path, 'foo:abc') self.assertEqual(request.kw, {'_anchor':'anchor'}) def test_it_rel(self): request = self._makeRequest() result = self._callFUT('abc', request, _app_url='') self.assertEqual(result, 'static url') self.assertEqual(request.path, 'pyramid.tests:abc') self.assertEqual(request.kw, {'_app_url':''}) class Test_static_path(unittest.TestCase): def _callFUT(self, path, request, **kw): from pyramid.url import static_path return static_path(path, request, **kw) def _makeRequest(self): class Request(object): def static_path(self, path, **kw): self.path = path self.kw = kw return 'static path' return Request() def test_it_abs(self): request = self._makeRequest() result = self._callFUT('/foo/bar/abc', request, _anchor='anchor') self.assertEqual(result, 'static path') self.assertEqual(request.path, '/foo/bar/abc') self.assertEqual(request.kw, {'_anchor':'anchor'}) def test_it_absspec(self): request = self._makeRequest() result = self._callFUT('foo:abc', request, _anchor='anchor') self.assertEqual(result, 'static path') self.assertEqual(request.path, 'foo:abc') self.assertEqual(request.kw, {'_anchor':'anchor'}) def test_it_rel(self): request = self._makeRequest() result = self._callFUT('abc', request, _app_url='') self.assertEqual(result, 'static path') self.assertEqual(request.path, 'pyramid.tests:abc') self.assertEqual(request.kw, {'_app_url':''}) class Test_current_route_url(unittest.TestCase): def _callFUT(self, request, *elements, **kw): from pyramid.url import current_route_url return current_route_url(request, *elements, **kw) def _makeRequest(self): class Request(object): def current_route_url(self, *elements, **kw): self.elements = elements self.kw = kw return 'current route url' return Request() def test_it(self): request = self._makeRequest() result = self._callFUT(request, 'abc', _app_url='') self.assertEqual(result, 'current route url') self.assertEqual(request.elements, ('abc',)) self.assertEqual(request.kw, {'_app_url':''}) class Test_current_route_path(unittest.TestCase): def _callFUT(self, request, *elements, **kw): from pyramid.url import current_route_path return current_route_path(request, *elements, **kw) def _makeRequest(self): class Request(object): def current_route_path(self, *elements, **kw): self.elements = elements self.kw = kw return 'current route path' return Request() def test_it(self): request = self._makeRequest() result = self._callFUT(request, 'abc', _anchor='abc') self.assertEqual(result, 'current route path') self.assertEqual(request.elements, ('abc',)) self.assertEqual(request.kw, {'_anchor':'abc'}) class DummyContext(object): def __init__(self, next=None): self.next = next 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 name = 'route' 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 def makeabs(*elements): if WIN: # pragma: no cover return r'c:\\' + os.path.sep.join(elements) else: return os.path.sep + os.path.sep.join(elements) pyramid-1.4.5/pyramid/tests/test_events.py0000664000175000017500000002362512203712502020210 0ustar takakitakakiimport unittest from pyramid import testing class NewRequestEventTests(unittest.TestCase): def _getTargetClass(self): from pyramid.events import NewRequest return NewRequest def _makeOne(self, request): return self._getTargetClass()(request) def test_class_conforms_to_INewRequest(self): from pyramid.interfaces import INewRequest from zope.interface.verify import verifyClass klass = self._getTargetClass() verifyClass(INewRequest, klass) def test_instance_conforms_to_INewRequest(self): from pyramid.interfaces import INewRequest from zope.interface.verify import verifyObject request = DummyRequest() inst = self._makeOne(request) verifyObject(INewRequest, inst) def test_ctor(self): request = DummyRequest() inst = self._makeOne(request) self.assertEqual(inst.request, request) class NewResponseEventTests(unittest.TestCase): def _getTargetClass(self): from pyramid.events import NewResponse return NewResponse def _makeOne(self, request, response): return self._getTargetClass()(request, response) def test_class_conforms_to_INewResponse(self): from pyramid.interfaces import INewResponse from zope.interface.verify import verifyClass klass = self._getTargetClass() verifyClass(INewResponse, klass) def test_instance_conforms_to_INewResponse(self): from pyramid.interfaces import INewResponse from zope.interface.verify import verifyObject request = DummyRequest() response = DummyResponse() inst = self._makeOne(request, response) verifyObject(INewResponse, inst) def test_ctor(self): request = DummyRequest() response = DummyResponse() inst = self._makeOne(request, response) self.assertEqual(inst.request, request) self.assertEqual(inst.response, response) class ApplicationCreatedEventTests(unittest.TestCase): def _getTargetClass(self): from pyramid.events import ApplicationCreated return ApplicationCreated def _makeOne(self, context=object()): return self._getTargetClass()(context) def test_class_conforms_to_IApplicationCreated(self): from pyramid.interfaces import IApplicationCreated from zope.interface.verify import verifyClass verifyClass(IApplicationCreated, self._getTargetClass()) def test_object_conforms_to_IApplicationCreated(self): from pyramid.interfaces import IApplicationCreated from zope.interface.verify import verifyObject verifyObject(IApplicationCreated, self._makeOne()) class WSGIApplicationCreatedEventTests(ApplicationCreatedEventTests): def _getTargetClass(self): from pyramid.events import WSGIApplicationCreatedEvent return WSGIApplicationCreatedEvent def test_class_conforms_to_IWSGIApplicationCreatedEvent(self): from pyramid.interfaces import IWSGIApplicationCreatedEvent from zope.interface.verify import verifyClass verifyClass(IWSGIApplicationCreatedEvent, self._getTargetClass()) def test_object_conforms_to_IWSGIApplicationCreatedEvent(self): from pyramid.interfaces import IWSGIApplicationCreatedEvent from zope.interface.verify import verifyObject verifyObject(IWSGIApplicationCreatedEvent, self._makeOne()) class ContextFoundEventTests(unittest.TestCase): def _getTargetClass(self): from pyramid.events import ContextFound return ContextFound def _makeOne(self, request=None): if request is None: request = DummyRequest() return self._getTargetClass()(request) def test_class_conforms_to_IContextFound(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IContextFound verifyClass(IContextFound, self._getTargetClass()) def test_instance_conforms_to_IContextFound(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IContextFound verifyObject(IContextFound, self._makeOne()) class AfterTraversalEventTests(ContextFoundEventTests): def _getTargetClass(self): from pyramid.events import AfterTraversal return AfterTraversal def test_class_conforms_to_IAfterTraversal(self): from zope.interface.verify import verifyClass from pyramid.interfaces import IAfterTraversal verifyClass(IAfterTraversal, self._getTargetClass()) def test_instance_conforms_to_IAfterTraversal(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IAfterTraversal verifyObject(IAfterTraversal, self._makeOne()) class TestSubscriber(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _makeOne(self, *ifaces, **predicates): from pyramid.events import subscriber return subscriber(*ifaces, **predicates) def test_register_single(self): from zope.interface import Interface class IFoo(Interface): pass class IBar(Interface): pass dec = self._makeOne(IFoo) def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.subscribed, [(foo, IFoo)]) def test_register_multi(self): from zope.interface import Interface class IFoo(Interface): pass class IBar(Interface): pass dec = self._makeOne(IFoo, IBar) def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.subscribed, [(foo, IFoo), (foo, IBar)]) def test_register_none_means_all(self): from zope.interface import Interface dec = self._makeOne() def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.subscribed, [(foo, Interface)]) def test_register_objectevent(self): from zope.interface import Interface class IFoo(Interface): pass class IBar(Interface): pass dec = self._makeOne([IFoo, IBar]) def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.subscribed, [(foo, [IFoo, IBar])]) def test___call__(self): dec = self._makeOne() dummy_venusian = DummyVenusian() dec.venusian = dummy_venusian def foo(): pass dec(foo) self.assertEqual(dummy_venusian.attached, [(foo, dec.register, 'pyramid')]) def test_regsister_with_predicates(self): from zope.interface import Interface dec = self._makeOne(a=1) def foo(): pass config = DummyConfigurator() scanner = Dummy() scanner.config = config dec.register(scanner, None, foo) self.assertEqual(config.subscribed, [(foo, Interface, {'a':1})]) class TestBeforeRender(unittest.TestCase): def _makeOne(self, system, val=None): from pyramid.events import BeforeRender return BeforeRender(system, val) def test_instance_conforms(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IBeforeRender event = self._makeOne({}) verifyObject(IBeforeRender, event) def test_setitem_success(self): event = self._makeOne({}) event['a'] = 1 self.assertEqual(event, {'a':1}) def test_setdefault_fail(self): event = self._makeOne({}) result = event.setdefault('a', 1) self.assertEqual(result, 1) self.assertEqual(event, {'a':1}) def test_setdefault_success(self): event = self._makeOne({}) event['a'] = 1 result = event.setdefault('a', 2) self.assertEqual(result, 1) self.assertEqual(event, {'a':1}) def test_update_success(self): event = self._makeOne({'a':1}) event.update({'b':2}) self.assertEqual(event, {'a':1, 'b':2}) def test__contains__True(self): system = {'a':1} event = self._makeOne(system) self.assertTrue('a' in event) def test__contains__False(self): system = {} event = self._makeOne(system) self.assertFalse('a' in event) def test__getitem__success(self): system = {'a':1} event = self._makeOne(system) self.assertEqual(event['a'], 1) def test__getitem__fail(self): system = {} event = self._makeOne(system) self.assertRaises(KeyError, event.__getitem__, 'a') def test_get_success(self): system = {'a':1} event = self._makeOne(system) self.assertEqual(event.get('a'), 1) def test_get_fail(self): system = {} event = self._makeOne(system) self.assertEqual(event.get('a'), None) def test_rendering_val(self): system = {} val = {} event = self._makeOne(system, val) self.assertTrue(event.rendering_val is val) class DummyConfigurator(object): def __init__(self): self.subscribed = [] def add_subscriber(self, wrapped, ifaces, **predicates): if not predicates: self.subscribed.append((wrapped, ifaces)) else: self.subscribed.append((wrapped, ifaces, predicates)) class DummyRegistry(object): pass class DummyVenusian(object): def __init__(self): self.attached = [] def attach(self, wrapped, fn, category=None): self.attached.append((wrapped, fn, category)) class Dummy: pass class DummyRequest: pass class DummyResponse: pass pyramid-1.4.5/pyramid/testing.py0000664000175000017500000005274312210154276016171 0ustar takakitakakiimport copy import os from contextlib import contextmanager from zope.interface import ( implementer, alsoProvides, ) from pyramid.interfaces import ( IRequest, IResponseFactory, ISession, ) from pyramid.compat import ( PY3, PYPY, class_types, ) from pyramid.config import Configurator from pyramid.decorator import reify from pyramid.response import Response from pyramid.registry import Registry from pyramid.security import ( Authenticated, Everyone, ) from pyramid.threadlocal import ( get_current_registry, manager, ) from pyramid.request import ( DeprecatedRequestMethodsMixin, CallbackMethodsMixin, ) from pyramid.url import URLMethodsMixin from pyramid.util import InstancePropertyMixin _marker = object() class DummyRootFactory(object): __parent__ = None __name__ = None def __init__(self, request): if 'bfg.routes.matchdict' in request: self.__dict__.update(request['bfg.routes.matchdict']) class DummySecurityPolicy(object): """ A standin for both an IAuthentication and IAuthorization policy """ def __init__(self, userid=None, groupids=(), permissive=True, remember_result=None, forget_result=None): self.userid = userid self.groupids = groupids self.permissive = permissive if remember_result is None: remember_result = [] if forget_result is None: forget_result = [] self.remember_result = remember_result self.forget_result = forget_result def authenticated_userid(self, request): return self.userid def unauthenticated_userid(self, request): return self.userid def effective_principals(self, request): effective_principals = [Everyone] if self.userid: effective_principals.append(Authenticated) effective_principals.append(self.userid) effective_principals.extend(self.groupids) return effective_principals def remember(self, request, principal, **kw): self.remembered = principal return self.remember_result def forget(self, request): self.forgotten = True return self.forget_result def permits(self, context, principals, permission): return self.permissive def principals_allowed_by_permission(self, context, permission): return self.effective_principals(None) class DummyTemplateRenderer(object): """ An instance of this class is returned from :meth:`pyramid.config.Configurator.testing_add_renderer`. It has a helper function (``assert_``) that makes it possible to make an assertion which compares data passed to the renderer by the view function against expected key/value pairs. """ def __init__(self, string_response=''): self._received = {} self._string_response = string_response self._implementation = MockTemplate(string_response) # For in-the-wild test code that doesn't create its own renderer, # but mutates our internals instead. When all you read is the # source code, *everything* is an API! def _get_string_response(self): return self._string_response def _set_string_response(self, response): self._string_response = response self._implementation.response = response string_response = property(_get_string_response, _set_string_response) def implementation(self): return self._implementation def __call__(self, kw, system=None): if system: self._received.update(system) self._received.update(kw) return self.string_response def __getattr__(self, k): """ Backwards compatibility """ val = self._received.get(k, _marker) if val is _marker: val = self._implementation._received.get(k, _marker) if val is _marker: raise AttributeError(k) return val def assert_(self, **kw): """ Accept an arbitrary set of assertion key/value pairs. For each assertion key/value pair assert that the renderer (eg. :func:`pyramid.renderer.render_to_response`) received the key with a value that equals the asserted value. If the renderer did not receive the key at all, or the value received by the renderer doesn't match the assertion value, raise an :exc:`AssertionError`.""" for k, v in kw.items(): myval = self._received.get(k, _marker) if myval is _marker: myval = self._implementation._received.get(k, _marker) if myval is _marker: raise AssertionError( 'A value for key "%s" was not passed to the renderer' % k) if myval != v: raise AssertionError( '\nasserted value for %s: %r\nactual value: %r' % ( k, v, myval)) return True class DummyResource: """ A dummy :app:`Pyramid` :term:`resource` object.""" def __init__(self, __name__=None, __parent__=None, __provides__=None, **kw): """ The resource's ``__name__`` attribute will be set to the value of the ``__name__`` argument, and the resource's ``__parent__`` attribute will be set to the value of the ``__parent__`` argument. If ``__provides__`` is specified, it should be an interface object or tuple of interface objects that will be attached to the resulting resource via :func:`zope.interface.alsoProvides`. Any extra keywords passed in the ``kw`` argumnent will be set as direct attributes of the resource object. .. note:: For backwards compatibility purposes, this class can also be imported as :class:`pyramid.testing.DummyModel`. """ self.__name__ = __name__ self.__parent__ = __parent__ if __provides__ is not None: alsoProvides(self, __provides__) self.kw = kw self.__dict__.update(**kw) self.subs = {} def __setitem__(self, name, val): """ When the ``__setitem__`` method is called, the object passed in as ``val`` will be decorated with a ``__parent__`` attribute pointing at the dummy resource and a ``__name__`` attribute that is the value of ``name``. The value will then be returned when dummy resource's ``__getitem__`` is called with the name ``name```.""" val.__name__ = name val.__parent__ = self self.subs[name] = val def __getitem__(self, name): """ Return a named subobject (see ``__setitem__``)""" ob = self.subs[name] return ob def __delitem__(self, name): del self.subs[name] def get(self, name, default=None): return self.subs.get(name, default) def values(self): """ Return the values set by __setitem__ """ return self.subs.values() def items(self): """ Return the items set by __setitem__ """ return self.subs.items() def keys(self): """ Return the keys set by __setitem__ """ return self.subs.keys() __iter__ = keys def __bool__(self): return True def __nonzero__(self): return True def __len__(self): return len(self.subs) def __contains__(self, name): return name in self.subs def clone(self, __name__=_marker, __parent__=_marker, **kw): """ Create a clone of the resource object. If ``__name__`` or ``__parent__`` arguments are passed, use these values to override the existing ``__name__`` or ``__parent__`` of the resource. If any extra keyword args are passed in via the ``kw`` argument, use these keywords to add to or override existing resource keywords (attributes).""" oldkw = self.kw.copy() oldkw.update(kw) inst = self.__class__(self.__name__, self.__parent__, **oldkw) inst.subs = copy.deepcopy(self.subs) if __name__ is not _marker: inst.__name__ = __name__ if __parent__ is not _marker: inst.__parent__ = __parent__ return inst DummyModel = DummyResource # b/w compat (forever) @implementer(ISession) class DummySession(dict): created = None new = True def changed(self): pass def invalidate(self): self.clear() def flash(self, msg, queue='', allow_duplicate=True): storage = self.setdefault('_f_' + queue, []) if allow_duplicate or (msg not in storage): storage.append(msg) def pop_flash(self, queue=''): storage = self.pop('_f_' + queue, []) return storage def peek_flash(self, queue=''): storage = self.get('_f_' + queue, []) return storage def new_csrf_token(self): token = '0123456789012345678901234567890123456789' self['_csrft_'] = token return token def get_csrf_token(self): token = self.get('_csrft_', None) if token is None: token = self.new_csrf_token() return token @implementer(IRequest) class DummyRequest(DeprecatedRequestMethodsMixin, URLMethodsMixin, CallbackMethodsMixin, InstancePropertyMixin): """ A DummyRequest object (incompletely) imitates a :term:`request` object. The ``params``, ``environ``, ``headers``, ``path``, and ``cookies`` arguments correspond to their :term:`WebOb` equivalents. The ``post`` argument, if passed, populates the request's ``POST`` attribute, but *not* ``params``, in order to allow testing that the app accepts data for a given view only from POST requests. This argument also sets ``self.method`` to "POST". Extra keyword arguments are assigned as attributes of the request itself. Note that DummyRequest does not have complete fidelity with a "real" request. For example, by default, the DummyRequest ``GET`` and ``POST`` attributes are of type ``dict``, unlike a normal Request's GET and POST, which are of type ``MultiDict``. If your code uses the features of MultiDict, you should either use a real :class:`pyramid.request.Request` or adapt your DummyRequest by replacing the attributes with ``MultiDict`` instances. Other similar incompatibilities exist. If you need all the features of a Request, use the :class:`pyramid.request.Request` class itself rather than this class while writing tests. """ method = 'GET' application_url = 'http://example.com' host = 'example.com:80' content_length = 0 query_string = '' charset = 'UTF-8' script_name = '' _registry = None def __init__(self, params=None, environ=None, headers=None, path='/', cookies=None, post=None, **kw): if environ is None: environ = {} if params is None: params = {} if headers is None: headers = {} if cookies is None: cookies = {} self.environ = environ self.headers = headers self.params = params self.cookies = cookies self.matchdict = {} self.GET = params if post is not None: self.method = 'POST' self.POST = post else: self.POST = params self.host_url = self.application_url self.path_url = self.application_url self.url = self.application_url self.path = path self.path_info = path self.script_name = '' self.path_qs = '' self.body = '' self.view_name = '' self.subpath = () self.traversed = () self.virtual_root_path = () self.context = None self.root = None self.virtual_root = None self.marshalled = params # repoze.monty self.session = DummySession() self.__dict__.update(kw) def _get_registry(self): if self._registry is None: return get_current_registry() return self._registry def _set_registry(self, registry): self._registry = registry def _del_registry(self): self._registry = None registry = property(_get_registry, _set_registry, _del_registry) @reify def response(self): f = self.registry.queryUtility(IResponseFactory, default=Response) return f() have_zca = True def setUp(registry=None, request=None, hook_zca=True, autocommit=True, settings=None): """ Set :app:`Pyramid` registry and request thread locals for the duration of a single unit test. Use this function in the ``setUp`` method of a unittest test case which directly or indirectly uses: - any method of the :class:`pyramid.config.Configurator` object returned by this function. - the :func:`pyramid.threadlocal.get_current_registry` or :func:`pyramid.threadlocal.get_current_request` functions. If you use the ``get_current_*`` functions (or call :app:`Pyramid` code that uses these functions) without calling ``setUp``, :func:`pyramid.threadlocal.get_current_registry` will return a *global* :term:`application registry`, which may cause unit tests to not be isolated with respect to registrations they perform. If the ``registry`` argument is ``None``, a new empty :term:`application registry` will be created (an instance of the :class:`pyramid.registry.Registry` class). If the ``registry`` argument is not ``None``, the value passed in should be an instance of the :class:`pyramid.registry.Registry` class or a suitable testing analogue. After ``setUp`` is finished, the registry returned by the :func:`pyramid.threadlocal.get_current_request` function will be the passed (or constructed) registry until :func:`pyramid.testing.tearDown` is called (or :func:`pyramid.testing.setUp` is called again) . If the ``hook_zca`` argument is ``True``, ``setUp`` will attempt to perform the operation ``zope.component.getSiteManager.sethook( pyramid.threadlocal.get_current_registry)``, which will cause the :term:`Zope Component Architecture` global API (e.g. :func:`zope.component.getSiteManager`, :func:`zope.component.getAdapter`, and so on) to use the registry constructed by ``setUp`` as the value it returns from :func:`zope.component.getSiteManager`. If the :mod:`zope.component` package cannot be imported, or if ``hook_zca`` is ``False``, the hook will not be set. If ``settings`` is not None, it must be a dictionary representing the values passed to a Configurator as its ``settings=`` argument. This function returns an instance of the :class:`pyramid.config.Configurator` class, which can be used for further configuration to set up an environment suitable for a unit or integration test. The ``registry`` attribute attached to the Configurator instance represents the 'current' :term:`application registry`; the same registry will be returned by :func:`pyramid.threadlocal.get_current_registry` during the execution of the test. """ manager.clear() if registry is None: registry = Registry('testing') config = Configurator(registry=registry, autocommit=autocommit) if settings is None: settings = {} if getattr(registry, 'settings', None) is None: config._set_settings(settings) if hasattr(registry, 'registerUtility'): # Sometimes nose calls us with a non-registry object because # it thinks this function is module test setup. Likewise, # someone may be passing us an esoteric "dummy" registry, and # the below won't succeed if it doesn't have a registerUtility # method. from pyramid.config import DEFAULT_RENDERERS for name, renderer in DEFAULT_RENDERERS: # Cause the default renderers to be registered because # in-the-wild test code relies on being able to call # e.g. ``pyramid.chameleon_zpt.render_template`` # without registering a .pt renderer, expecting the "real" # template to be rendered. This is a holdover from when # individual template system renderers weren't indirected # by the ``pyramid.renderers`` machinery, and # ``render_template`` and friends went behind the back of # any existing renderer factory lookup system. config.add_renderer(name, renderer) config.add_default_view_predicates() config.add_default_route_predicates() config.commit() global have_zca try: have_zca and hook_zca and config.hook_zca() except ImportError: # pragma: no cover # (dont choke on not being able to import z.component) have_zca = False config.begin(request=request) return config def tearDown(unhook_zca=True): """Undo the effects :func:`pyramid.testing.setUp`. Use this function in the ``tearDown`` method of a unit test that uses :func:`pyramid.testing.setUp` in its ``setUp`` method. If the ``unhook_zca`` argument is ``True`` (the default), call :func:`zope.component.getSiteManager.reset`. This undoes the action of :func:`pyramid.testing.setUp` called with the argument ``hook_zca=True``. If :mod:`zope.component` cannot be imported, ignore the argument. """ global have_zca if unhook_zca and have_zca: try: from zope.component import getSiteManager getSiteManager.reset() except ImportError: # pragma: no cover have_zca = False info = manager.pop() manager.clear() if info is not None: registry = info['registry'] if hasattr(registry, '__init__') and hasattr(registry, '__name__'): try: registry.__init__(registry.__name__) except TypeError: # calling __init__ is largely for the benefit of # people who want to use the global ZCA registry; # however maybe somebody's using a registry we don't # understand, let's not blow up pass def cleanUp(*arg, **kw): """ :func:`pyramid.testing.cleanUp` is an alias for :func:`pyramid.testing.setUp`. """ return setUp(*arg, **kw) class DummyRendererFactory(object): """ Registered by :meth:`pyramid.config.Configurator.testing_add_renderer` as a dummy renderer factory. The indecision about what to use as a key (a spec vs. a relative name) is caused by test suites in the wild believing they can register either. The ``factory`` argument passed to this constructor is usually the *real* template renderer factory, found when ``testing_add_renderer`` is called.""" def __init__(self, name, factory): self.name = name self.factory = factory # the "real" renderer factory reg'd previously self.renderers = {} def add(self, spec, renderer): self.renderers[spec] = renderer if ':' in spec: package, relative = spec.split(':', 1) self.renderers[relative] = renderer def __call__(self, info): spec = info.name renderer = self.renderers.get(spec) if renderer is None: if ':' in spec: package, relative = spec.split(':', 1) renderer = self.renderers.get(relative) if renderer is None: if self.factory: renderer = self.factory(info) else: raise KeyError('No testing renderer registered for %r' % spec) return renderer class MockTemplate(object): def __init__(self, response): self._received = {} self.response = response def __getattr__(self, attrname): return self def __getitem__(self, attrname): return self def __call__(self, *arg, **kw): self._received.update(kw) return self.response def skip_on(*platforms): # pragma: no cover skip = False for platform in platforms: if skip_on.os_name.startswith(platform): skip = True if platform == 'pypy' and PYPY: skip = True if platform == 'py3' and PY3: skip = True def decorator(func): if isinstance(func, class_types): if skip: return None else: return func else: def wrapper(*args, **kw): if skip: return return func(*args, **kw) wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ return wrapper return decorator skip_on.os_name = os.name # for testing @contextmanager def testConfig(registry=None, request=None, hook_zca=True, autocommit=True, settings=None): """Returns a context manager for test set up. This context manager calls :func:`pyramid.testing.testSetup` when entering and :func:`pyramid.testing.tearDown` when exiting. All arguments are passed directly to :func:`pyramid.testing.testSetup`. If the ZCA is hooked, it will always be un-hooked in tearDown. This context manager allows you to write test code like this: .. code-block:: python :linenos: with testConfig() as config: config.add_route('bar', '/bar/{id}') req = DummyRequest() resp = myview(req), """ config = setUp(registry=registry, request=request, hook_zca=hook_zca, autocommit=autocommit, settings=settings) try: yield config finally: tearDown(unhook_zca=hook_zca) pyramid-1.4.5/pyramid/encode.py0000664000175000017500000000400312203712502015725 0ustar takakitakakifrom pyramid.compat import ( text_type, binary_type, is_nonstr_iter, url_quote as _url_quote, url_quote_plus as quote_plus, # bw compat api (dnr) ) def url_quote(s, safe=''): # bw compat api return _url_quote(s, safe=safe) def urlencode(query, doseq=True): """ An alternate implementation of Python's stdlib `urllib.urlencode function `_ which accepts unicode keys and values within the ``query`` dict/sequence; all Unicode keys and values are first converted to UTF-8 before being used to compose the query string. The value of ``query`` must be a sequence of two-tuples representing key/value pairs *or* an object (often a dictionary) with an ``.items()`` method that returns a sequence of two-tuples representing key/value pairs. For minimal calling convention backwards compatibility, this version of urlencode accepts *but ignores* a second argument conventionally named ``doseq``. The Python stdlib version behaves differently when ``doseq`` is False and when a sequence is presented as one of the values. This version always behaves in the ``doseq=True`` mode, no matter what the value of the second argument. See the Python stdlib documentation for ``urllib.urlencode`` for more information. """ try: # presumed to be a dictionary query = query.items() except AttributeError: pass result = '' prefix = '' for (k, v) in query: k = _enc(k) if is_nonstr_iter(v): for x in v: x = _enc(x) result += '%s%s=%s' % (prefix, k, x) prefix = '&' else: v = _enc(v) result += '%s%s=%s' % (prefix, k, v) prefix = '&' return result def _enc(val): cls = val.__class__ if cls is text_type: val = val.encode('utf-8') elif cls is not binary_type: val = str(val).encode('utf-8') return quote_plus(val) pyramid-1.4.5/pyramid/response.py0000664000175000017500000001221512210154276016340 0ustar takakitakakiimport mimetypes from os.path import ( getmtime, getsize, ) import venusian from webob import Response as _Response from zope.interface import implementer from pyramid.interfaces import IResponse def init_mimetypes(mimetypes): # this is a function so it can be unittested if hasattr(mimetypes, 'init'): mimetypes.init() return True return False # See http://bugs.python.org/issue5853 which is a recursion bug # that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix # has been applied on the Python 2 trunk). init_mimetypes(mimetypes) _BLOCK_SIZE = 4096 * 64 # 256K @implementer(IResponse) class Response(_Response): pass class FileResponse(Response): """ A Response object that can be used to serve a static file from disk simply. ``path`` is a file path on disk. ``request`` must be a Pyramid :term:`request` object if passed. Note that a request *must* be passed if the response is meant to attempt to use the ``wsgi.file_wrapper`` feature of the web server that you're using to serve your Pyramid application. ``cache_max_age`` if passed, is the number of seconds that should be used to HTTP cache this response. ``content_type``, if passed, is the content_type of the response. ``content_encoding``, if passed is the content_encoding of the response. It's generally safe to leave this set to ``None`` if you're serving a binary file. This argument will be ignored if you don't also pass ``content-type``. """ def __init__(self, path, request=None, cache_max_age=None, content_type=None, content_encoding=None): super(FileResponse, self).__init__(conditional_response=True) self.last_modified = getmtime(path) if content_type is None: content_type, content_encoding = mimetypes.guess_type(path, strict=False) if content_type is None: content_type = 'application/octet-stream' self.content_type = content_type self.content_encoding = content_encoding content_length = getsize(path) f = open(path, 'rb') app_iter = None if request is not None: environ = request.environ if 'wsgi.file_wrapper' in environ: app_iter = environ['wsgi.file_wrapper'](f, _BLOCK_SIZE) if app_iter is None: app_iter = FileIter(f, _BLOCK_SIZE) self.app_iter = app_iter # assignment of content_length must come after assignment of app_iter self.content_length = content_length if cache_max_age is not None: self.cache_expires = cache_max_age class FileIter(object): """ A fixed-block-size iterator for use as a WSGI app_iter. ``file`` is a Python file pointer (or at least an object with a ``read`` method that takes a size hint). ``block_size`` is an optional block size for iteration. """ def __init__(self, file, block_size=_BLOCK_SIZE): self.file = file self.block_size = block_size def __iter__(self): return self def next(self): val = self.file.read(self.block_size) if not val: raise StopIteration return val __next__ = next # py3 def close(self): self.file.close() class response_adapter(object): """ Decorator activated via a :term:`scan` which treats the function being decorated as a :term:`response adapter` for the set of types or interfaces passed as ``*types_or_ifaces`` to the decorator constructor. For example, if you scan the following response adapter: .. code-block:: python from pyramid.response import Response from pyramid.response import response_adapter @response_adapter(int) def myadapter(i): return Response(status=i) You can then return an integer from your view callables, and it will be converted into a response with the integer as the status code. More than one type or interface can be passed as a constructor argument. The decorated response adapter will be called for each type or interface. .. code-block:: python import json from pyramid.response import Response from pyramid.response import response_adapter @response_adapter(dict, list) def myadapter(ob): return Response(json.dumps(ob)) This method will have no effect until a :term:`scan` is performed agains the package or module which contains it, ala: .. code-block:: python from pyramid.config import Configurator config = Configurator() config.scan('somepackage_containing_adapters') """ venusian = venusian # for unit testing def __init__(self, *types_or_ifaces): self.types_or_ifaces = types_or_ifaces def register(self, scanner, name, wrapped): config = scanner.config for type_or_iface in self.types_or_ifaces: config.add_response_adapter(wrapped, type_or_iface) def __call__(self, wrapped): self.venusian.attach(wrapped, self.register, category='pyramid') return wrapped pyramid-1.4.5/pyramid/url.py0000664000175000017500000010103412210154276015302 0ustar takakitakaki""" Utility functions for dealing with URLs in pyramid """ import os import warnings from repoze.lru import lru_cache from pyramid.interfaces import ( IResourceURL, IRoutesMapper, IStaticURLInfo, ) from pyramid.compat import ( native_, bytes_, text_type, url_quote, ) from pyramid.encode import urlencode from pyramid.path import caller_package from pyramid.threadlocal import get_current_registry from pyramid.traversal import ( ResourceURL, quote_path_segment, ) PATH_SAFE = '/:@&+$,' # from webob class URLMethodsMixin(object): """ Request methods mixin for BaseRequest having to do with URL generation """ def _partial_application_url(self, scheme=None, host=None, port=None): """ Construct the URL defined by request.application_url, replacing any of the default scheme, host, or port portions with user-supplied variants. If ``scheme`` is passed as ``https``, and the ``port`` is *not* passed, the ``port`` value is assumed to ``443``. Likewise, if ``scheme`` is passed as ``http`` and ``port`` is not passed, the ``port`` value is assumed to be ``80``. """ e = self.environ if scheme is None: scheme = e['wsgi.url_scheme'] else: if scheme == 'https': if port is None: port = '443' if scheme == 'http': if port is None: port = '80' url = scheme + '://' if port is not None: port = str(port) if host is None: host = e.get('HTTP_HOST') if host is None: host = e['SERVER_NAME'] if port is None: if ':' in host: host, port = host.split(':', 1) else: port = e['SERVER_PORT'] else: if ':' in host: host, _ = host.split(':', 1) if scheme == 'https': if port == '443': port = None elif scheme == 'http': if port == '80': port = None url += host if port: url += ':%s' % port url_encoding = getattr(self, 'url_encoding', 'utf-8') # webob 1.2b3+ bscript_name = bytes_(self.script_name, url_encoding) return url + url_quote(bscript_name, PATH_SAFE) def route_url(self, route_name, *elements, **kw): """Generates a fully qualified URL for a named :app:`Pyramid` :term:`route configuration`. Use the route's ``name`` as the first positional argument. Additional positional arguments (``*elements``) are appended to the URL as path segments after it is generated. Use keyword arguments to supply values which match any dynamic path elements in the route definition. Raises a :exc:`KeyError` exception if the URL cannot be generated for any reason (not enough arguments, for example). For example, if you've defined a route named "foobar" with the path ``{foo}/{bar}/*traverse``:: request.route_url('foobar', foo='1') => request.route_url('foobar', foo='1', bar='2') => request.route_url('foobar', foo='1', bar='2', traverse=('a','b')) => http://e.com/1/2/a/b request.route_url('foobar', foo='1', bar='2', traverse='/a/b') => http://e.com/1/2/a/b Values replacing ``:segment`` arguments can be passed as strings or Unicode objects. They will be encoded to UTF-8 and URL-quoted before being placed into the generated URL. Values replacing ``*remainder`` arguments can be passed as strings *or* tuples of Unicode/string values. If a tuple is passed as a ``*remainder`` replacement value, its values are URL-quoted and encoded to UTF-8. The resulting strings are joined with slashes and rendered into the URL. If a string is passed as a ``*remainder`` replacement value, it is tacked on to the URL after being URL-quoted-except-for-embedded-slashes. If a keyword argument ``_query`` is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of ``_query`` must be a sequence of two-tuples *or* a data structure with an ``.items()`` method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of :func:`pyramid.encode.urlencode` function. After the query data is turned into a query string, a leading ``?`` is prepended, and the resulting string is appended to the generated URL. .. note:: Python data structures that are passed as ``_query`` which are sequences or dictionaries are turned into a string under the same rules as when run through :func:`urllib.urlencode` with the ``doseq`` argument equal to ``True``. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument ``_anchor`` is present, its string representation will be used as a named anchor in the generated URL (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is ``http://example.com/route/url``, the resulting generated URL will be ``http://example.com/route/url#foo``). .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 encoded. If ``_anchor`` is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL. If both ``_anchor`` and ``_query`` are specified, the anchor element will always follow the query element, e.g. ``http://example.com?foo=1#bar``. If any of the keyword arguments ``_scheme``, ``_host``, or ``_port`` is passed and is non-``None``, the provided value will replace the named portion in the generated URL. For example, if you pass ``_host='foo.com'``, and the URL that would have been generated without the host replacement is ``http://example.com/a``, the result will be ``https://foo.com/a``. Note that if ``_scheme`` is passed as ``https``, and ``_port`` is not passed, the ``_port`` value is assumed to have been passed as ``443``. Likewise, if ``_scheme`` is passed as ``http`` and ``_port`` is not passed, the ``_port`` value is assumed to have been passed as ``80``. To avoid this behavior, always explicitly pass ``_port`` whenever you pass ``_scheme``. If a keyword ``_app_url`` is present, it will be used as the protocol/hostname/port/leading path prefix of the generated URL. For example, using an ``_app_url`` of ``http://example.com:8080/foo`` would cause the URL ``http://example.com:8080/foo/fleeb/flub`` to be returned from this function if the expansion of the route pattern associated with the ``route_name`` expanded to ``/fleeb/flub``. If ``_app_url`` is not specified, the result of ``request.application_url`` will be used as the prefix (the default). If both ``_app_url`` and any of ``_scheme``, ``_host``, or ``_port`` are passed, ``_app_url`` takes precedence and any values passed for ``_scheme``, ``_host``, and ``_port`` will be ignored. This function raises a :exc:`KeyError` if the URL cannot be generated due to missing replacement names. Extra replacement names are ignored. If the route object which matches the ``route_name`` argument has a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments passed to this function might be augmented or changed. """ try: reg = self.registry except AttributeError: reg = get_current_registry() # b/c mapper = reg.getUtility(IRoutesMapper) route = mapper.get_route(route_name) if route is None: raise KeyError('No such route named %s' % route_name) if route.pregenerator is not None: elements, kw = route.pregenerator(self, elements, kw) anchor = '' qs = '' app_url = None host = None scheme = None port = None if '_query' in kw: query = kw.pop('_query') if query: qs = '?' + urlencode(query, doseq=True) if '_anchor' in kw: anchor = kw.pop('_anchor') anchor = native_(anchor, 'utf-8') anchor = '#' + anchor if '_app_url' in kw: app_url = kw.pop('_app_url') if '_host' in kw: host = kw.pop('_host') if '_scheme' in kw: scheme = kw.pop('_scheme') if '_port' in kw: port = kw.pop('_port') if app_url is None: if (scheme is not None or host is not None or port is not None): app_url = self._partial_application_url(scheme, host, port) else: app_url = self.application_url path = route.generate(kw) # raises KeyError if generate fails if elements: suffix = _join_elements(elements) if not path.endswith('/'): suffix = '/' + suffix else: suffix = '' return app_url + path + suffix + qs + anchor def route_path(self, route_name, *elements, **kw): """ Generates a path (aka a 'relative URL', a URL minus the host, scheme, and port) for a named :app:`Pyramid` :term:`route configuration`. This function accepts the same argument as :meth:`pyramid.request.Request.route_url` and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. For example, if you've defined a route named 'foobar' with the path ``/{foo}/{bar}``, this call to ``route_path``:: request.route_path('foobar', foo='1', bar='2') Will return the string ``/1/2``. .. note:: Calling ``request.route_path('route')`` is the same as calling ``request.route_url('route', _app_url=request.script_name)``. :meth:`pyramid.request.Request.route_path` is, in fact, implemented in terms of :meth:`pyramid.request.Request.route_url` in just this way. As a result, any ``_app_url`` passed within the ``**kw`` values to ``route_path`` will be ignored. """ kw['_app_url'] = self.script_name return self.route_url(route_name, *elements, **kw) def resource_url(self, resource, *elements, **kw): """ Generate a string representing the absolute URL of the :term:`resource` object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or ``SERVER_NAME`` in the request, plus any ``SCRIPT_NAME``. The overall result of this method is always a UTF-8 encoded string. Examples:: request.resource_url(resource) => http://example.com/ request.resource_url(resource, 'a.html') => http://example.com/a.html request.resource_url(resource, 'a.html', query={'q':'1'}) => http://example.com/a.html?q=1 request.resource_url(resource, 'a.html', anchor='abc') => http://example.com/a.html#abc request.resource_url(resource, app_url='') => / Any positional arguments passed in as ``elements`` must be strings Unicode objects, or integer objects. These will be joined by slashes and appended to the generated resource URL. Each of the elements passed in is URL-quoted before being appended; if any element is Unicode, it will converted to a UTF-8 bytestring before being URL-quoted. If any element is an integer, it will be converted to its string representation before being URL-quoted. .. warning:: if no ``elements`` arguments are specified, the resource URL will end with a trailing slash. If any ``elements`` are used, the generated URL will *not* end in trailing a slash. If a keyword argument ``query`` is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of ``query`` must be a sequence of two-tuples *or* a data structure with an ``.items()`` method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of ``pyramid.url.urlencode`` function. After the query data is turned into a query string, a leading ``?`` is prepended, and the resulting string is appended to the generated URL. .. note:: Python data structures that are passed as ``query`` which are sequences or dictionaries are turned into a string under the same rules as when run through :func:`urllib.urlencode` with the ``doseq`` argument equal to ``True``. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument ``anchor`` is present, its string representation will be used as a named anchor in the generated URL (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is ``http://example.com/resource/url``, the resulting generated URL will be ``http://example.com/resource/url#foo``). .. note:: If ``anchor`` is passed as a string, it should be UTF-8 encoded. If ``anchor`` is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL. If both ``anchor`` and ``query`` are specified, the anchor element will always follow the query element, e.g. ``http://example.com?foo=1#bar``. If any of the keyword arguments ``scheme``, ``host``, or ``port`` is passed and is non-``None``, the provided value will replace the named portion in the generated URL. For example, if you pass ``host='foo.com'``, and the URL that would have been generated without the host replacement is ``http://example.com/a``, the result will be ``https://foo.com/a``. If ``scheme`` is passed as ``https``, and an explicit ``port`` is not passed, the ``port`` value is assumed to have been passed as ``443``. Likewise, if ``scheme`` is passed as ``http`` and ``port`` is not passed, the ``port`` value is assumed to have been passed as ``80``. To avoid this behavior, always explicitly pass ``port`` whenever you pass ``scheme``. If a keyword argument ``app_url`` is passed and is not ``None``, it should be a string that will be used as the port/hostname/initial path portion of the generated URL instead of the default request application URL. For example, if ``app_url='http://foo'``, then the resulting url of a resource that has a path of ``/baz/bar`` will be ``http://foo/baz/bar``. If you want to generate completely relative URLs with no leading scheme, host, port, or initial path, you can pass ``app_url=''`. Passing ``app_url=''` when the resource path is ``/baz/bar`` will return ``/baz/bar``. .. note:: ``app_url`` is new as of Pyramid 1.3. If ``app_url`` is passed and any of ``scheme``, ``port``, or ``host`` are also passed, ``app_url`` will take precedence and the values passed for ``scheme``, ``host``, and/or ``port`` will be ignored. If the ``resource`` passed in has a ``__resource_url__`` method, it will be used to generate the URL (scheme, host, port, path) that for the base resource which is operated upon by this function. See also :ref:`overriding_resource_url_generation`. .. note:: If the :term:`resource` used is the result of a :term:`traversal`, it must be :term:`location`-aware. The resource can also be the context of a :term:`URL dispatch`; contexts found this way do not need to be location-aware. .. note:: If a 'virtual root path' is present in the request environment (the value of the WSGI environ key ``HTTP_X_VHM_ROOT``), and the resource was obtained via :term:`traversal`, the URL path will not include the virtual root prefix (it will be stripped off the left hand side of the generated URL). .. note:: For backwards compatibility purposes, this method is also aliased as the ``model_url`` method of request. """ try: reg = self.registry except AttributeError: reg = get_current_registry() # b/c url_adapter = reg.queryMultiAdapter((resource, self), IResourceURL) if url_adapter is None: url_adapter = ResourceURL(resource, self) virtual_path = getattr(url_adapter, 'virtual_path', None) if virtual_path is None: # old-style IContextURL adapter (Pyramid 1.2 and previous) warnings.warn( 'Pyramid is using an IContextURL adapter to generate a ' 'resource URL; any "app_url", "host", "port", or "scheme" ' 'arguments passed to resource_url are being ignored. To ' 'avoid this behavior, as of Pyramid 1.3, register an ' 'IResourceURL adapter instead of an IContextURL ' 'adapter for the resource type(s). IContextURL adapters ' 'will be ignored in a later major release of Pyramid.', DeprecationWarning, 2) resource_url = url_adapter() else: # newer-style IResourceURL adapter (Pyramid 1.3 and after) app_url = None scheme = None host = None port = None if 'app_url' in kw: app_url = kw['app_url'] if 'scheme' in kw: scheme = kw['scheme'] if 'host' in kw: host = kw['host'] if 'port' in kw: port = kw['port'] if app_url is None: if scheme or host or port: app_url = self._partial_application_url(scheme, host, port) else: app_url = self.application_url resource_url = None local_url = getattr(resource, '__resource_url__', None) if local_url is not None: # the resource handles its own url generation d = dict( virtual_path = virtual_path, physical_path = url_adapter.physical_path, app_url = app_url, ) # allow __resource_url__ to punt by returning None resource_url = local_url(self, d) if resource_url is None: # the resource did not handle its own url generation or the # __resource_url__ function returned None resource_url = app_url + virtual_path qs = '' anchor = '' if 'query' in kw: query = kw['query'] if query: qs = '?' + urlencode(query, doseq=True) if 'anchor' in kw: anchor = kw['anchor'] if isinstance(anchor, text_type): anchor = native_(anchor, 'utf-8') anchor = '#' + anchor if elements: suffix = _join_elements(elements) else: suffix = '' return resource_url + suffix + qs + anchor model_url = resource_url # b/w compat forever def resource_path(self, resource, *elements, **kw): """ Generates a path (aka a 'relative URL', a URL minus the host, scheme, and port) for a :term:`resource`. This function accepts the same argument as :meth:`pyramid.request.Request.resource_url` and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. .. note:: Calling ``request.resource_path(resource)`` is the same as calling ``request.resource_path(resource, app_url=request.script_name)``. :meth:`pyramid.request.Request.resource_path` is, in fact, implemented in terms of :meth:`pyramid.request.Request.resource_url` in just this way. As a result, any ``app_url`` passed within the ``**kw`` values to ``route_path`` will be ignored. ``scheme``, ``host``, and ``port`` are also ignored. """ kw['app_url'] = self.script_name return self.resource_url(resource, *elements, **kw) def static_url(self, path, **kw): """ Generates a fully qualified URL for a static :term:`asset`. The asset must live within a location defined via the :meth:`pyramid.config.Configurator.add_static_view` :term:`configuration declaration` (see :ref:`static_assets_section`). Example:: request.static_url('mypackage:static/foo.css') => http://example.com/static/foo.css The ``path`` argument points at a file or directory on disk which a URL should be generated for. The ``path`` may be either a relative path (e.g. ``static/foo.css``) or an absolute path (e.g. ``/abspath/to/static/foo.css``) or a :term:`asset specification` (e.g. ``mypackage:static/foo.css``). The purpose of the ``**kw`` argument is the same as the purpose of the :meth:`pyramid.request.Request.route_url` ``**kw`` argument. See the documentation for that function to understand the arguments which you can provide to it. However, typically, you don't need to pass anything as ``*kw`` when generating a static asset URL. This function raises a :exc:`ValueError` if a static view definition cannot be found which matches the path specification. """ if not os.path.isabs(path): if not ':' in path: # if it's not a package:relative/name and it's not an # /absolute/path it's a relative/path; this means its relative # to the package in which the caller's module is defined. package = caller_package() path = '%s:%s' % (package.__name__, path) try: reg = self.registry except AttributeError: reg = get_current_registry() # b/c info = reg.queryUtility(IStaticURLInfo) if info is None: raise ValueError('No static URL definition matching %s' % path) return info.generate(path, self, **kw) def static_path(self, path, **kw): """ Generates a path (aka a 'relative URL', a URL minus the host, scheme, and port) for a static resource. This function accepts the same argument as :meth:`pyramid.request.Request.static_url` and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. Example:: request.static_path('mypackage:static/foo.css') => /static/foo.css .. note:: Calling ``request.static_path(apath)`` is the same as calling ``request.static_url(apath, _app_url=request.script_name)``. :meth:`pyramid.request.Request.static_path` is, in fact, implemented in terms of `:meth:`pyramid.request.Request.static_url` in just this way. As a result, any ``_app_url`` passed within the ``**kw`` values to ``static_path`` will be ignored. """ if not os.path.isabs(path): if not ':' in path: # if it's not a package:relative/name and it's not an # /absolute/path it's a relative/path; this means its relative # to the package in which the caller's module is defined. package = caller_package() path = '%s:%s' % (package.__name__, path) kw['_app_url'] = self.script_name return self.static_url(path, **kw) def current_route_url(self, *elements, **kw): """ Generates a fully qualified URL for a named :app:`Pyramid` :term:`route configuration` based on the 'current route'. This function supplements :meth:`pyramid.request.Request.route_url`. It presents an easy way to generate a URL for the 'current route' (defined as the route which matched when the request was generated). The arguments to this method have the same meaning as those with the same names passed to :meth:`pyramid.request.Request.route_url`. It also understands an extra argument which ``route_url`` does not named ``_route_name``. The route name used to generate a URL is taken from either the ``_route_name`` keyword argument or the name of the route which is currently associated with the request if ``_route_name`` was not passed. Keys and values from the current request :term:`matchdict` are combined with the ``kw`` arguments to form a set of defaults named ``newkw``. Then ``request.route_url(route_name, *elements, **newkw)`` is called, returning a URL. Examples follow. If the 'current route' has the route pattern ``/foo/{page}`` and the current url path is ``/foo/1`` , the matchdict will be ``{'page':'1'}``. The result of ``request.current_route_url()`` in this situation will be ``/foo/1``. If the 'current route' has the route pattern ``/foo/{page}`` and the current url path is ``/foo/1``, the matchdict will be ``{'page':'1'}``. The result of ``request.current_route_url(page='2')`` in this situation will be ``/foo/2``. Usage of the ``_route_name`` keyword argument: if our routing table defines routes ``/foo/{action}`` named 'foo' and ``/foo/{action}/{page}`` named ``fooaction``, and the current url pattern is ``/foo/view`` (which has matched the ``/foo/{action}`` route), we may want to use the matchdict args to generate a URL to the ``fooaction`` route. In this scenario, ``request.current_route_url(_route_name='fooaction', page='5')`` Will return string like: ``/foo/view/5``. """ if '_route_name' in kw: route_name = kw.pop('_route_name') else: route = getattr(self, 'matched_route', None) route_name = getattr(route, 'name', None) if route_name is None: raise ValueError('Current request matches no route') newkw = {} newkw.update(self.matchdict) newkw.update(kw) return self.route_url(route_name, *elements, **newkw) def current_route_path(self, *elements, **kw): """ Generates a path (aka a 'relative URL', a URL minus the host, scheme, and port) for the :app:`Pyramid` :term:`route configuration` matched by the current request. This function accepts the same argument as :meth:`pyramid.request.Request.current_route_url` and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. For example, if the route matched by the current request has the pattern ``/{foo}/{bar}``, this call to ``current_route_path``:: request.current_route_path(foo='1', bar='2') Will return the string ``/1/2``. .. note:: Calling ``request.current_route_path('route')`` is the same as calling ``request.current_route_url('route', _app_url=request.script_name)``. :meth:`pyramid.request.Request.current_route_path` is, in fact, implemented in terms of :meth:`pyramid.request.Request.current_route_url` in just this way. As a result, any ``_app_url`` passed within the ``**kw`` values to ``current_route_path`` will be ignored. """ kw['_app_url'] = self.script_name return self.current_route_url(*elements, **kw) def route_url(route_name, request, *elements, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.route_url(route_name, *elements, **kw) See :meth:`pyramid.request.Request.route_url` for more information. """ return request.route_url(route_name, *elements, **kw) def route_path(route_name, request, *elements, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.route_path(route_name, *elements, **kw) See :meth:`pyramid.request.Request.route_path` for more information. """ return request.route_path(route_name, *elements, **kw) def resource_url(resource, request, *elements, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.resource_url(resource, *elements, **kw) See :meth:`pyramid.request.Request.resource_url` for more information. """ return request.resource_url(resource, *elements, **kw) model_url = resource_url # b/w compat (forever) def static_url(path, request, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.static_url(path, **kw) See :meth:`pyramid.request.Request.static_url` for more information. """ if not os.path.isabs(path): if not ':' in path: # if it's not a package:relative/name and it's not an # /absolute/path it's a relative/path; this means its relative # to the package in which the caller's module is defined. package = caller_package() path = '%s:%s' % (package.__name__, path) return request.static_url(path, **kw) def static_path(path, request, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.static_path(path, **kw) See :meth:`pyramid.request.Request.static_path` for more information. """ if not os.path.isabs(path): if not ':' in path: # if it's not a package:relative/name and it's not an # /absolute/path it's a relative/path; this means its relative # to the package in which the caller's module is defined. package = caller_package() path = '%s:%s' % (package.__name__, path) return request.static_path(path, **kw) def current_route_url(request, *elements, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.current_route_url(*elements, **kw) See :meth:`pyramid.request.Request.current_route_url` for more information. """ return request.current_route_url(*elements, **kw) def current_route_path(request, *elements, **kw): """ This is a backwards compatibility function. Its result is the same as calling:: request.current_route_path(*elements, **kw) See :meth:`pyramid.request.Request.current_route_path` for more information. """ return request.current_route_path(*elements, **kw) @lru_cache(1000) def _join_elements(elements): return '/'.join([quote_path_segment(s, safe=':@&+$,') for s in elements]) pyramid-1.4.5/pyramid/scaffolds/0000775000175000017500000000000012210157153016070 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/tests.py0000664000175000017500000000612712203712502017607 0ustar takakitakakiimport sys import os import pkg_resources import shutil import subprocess import tempfile import time try: import httplib except ImportError: # pragma: no cover import http.client as httplib from pyramid.compat import PY3 class TemplateTest(object): def make_venv(self, directory): # pragma: no cover import virtualenv import sys from virtualenv import Logger logger = Logger([(Logger.level_for_integer(2), sys.stdout)]) virtualenv.logger = logger virtualenv.create_environment(directory, site_packages=False, clear=False, unzip_setuptools=True, use_distribute=PY3) def install(self, tmpl_name): # pragma: no cover try: self.old_cwd = os.getcwd() self.directory = tempfile.mkdtemp() self.make_venv(self.directory) os.chdir(pkg_resources.get_distribution('pyramid').location) subprocess.check_call( [os.path.join(self.directory, 'bin', 'python'), 'setup.py', 'develop']) os.chdir(self.directory) subprocess.check_call(['bin/pcreate', '-s', tmpl_name, 'Dingle']) os.chdir('Dingle') py = os.path.join(self.directory, 'bin', 'python') subprocess.check_call([py, 'setup.py', 'install']) if tmpl_name == 'alchemy': populate = os.path.join(self.directory, 'bin', 'initialize_Dingle_db') subprocess.check_call([populate, 'development.ini']) subprocess.check_call([py, 'setup.py', 'test']) pserve = os.path.join(self.directory, 'bin', 'pserve') for ininame, hastoolbar in (('development.ini', True), ('production.ini', False)): proc = subprocess.Popen([pserve, ininame]) try: time.sleep(5) proc.poll() if proc.returncode is not None: raise RuntimeError('%s didnt start' % ininame) conn = httplib.HTTPConnection('localhost:6543') conn.request('GET', '/') resp = conn.getresponse() assert resp.status == 200, ininame data = resp.read() toolbarchunk = b'
= (2, 6) and sys.version_info < (3, 0): templates.append('zodb') for name in templates: test = TemplateTest() test.install(name) pyramid-1.4.5/pyramid/scaffolds/template.py0000664000175000017500000001266212210154276020267 0ustar takakitakaki# (c) 2005 Ian Bicking and contributors; written for Paste # (http://pythonpaste.org) Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php import re import sys import os from pyramid.compat import ( native_, bytes_, ) from pyramid.scaffolds import copydir fsenc = sys.getfilesystemencoding() class Template(object): """ Inherit from this base class and override methods to use the Pyramid scaffolding system.""" copydir = copydir # for testing _template_dir = None def __init__(self, name): self.name = name def render_template(self, content, vars, filename=None): """ Return a bytestring representing a templated file based on the input (content) and the variable names defined (vars). ``filename`` is used for exception reporting.""" # this method must not be named "template_renderer" fbo of extension # scaffolds that need to work under pyramid 1.2 and 1.3, and which # need to do "template_renderer = # staticmethod(paste_script_template_renderer)" content = native_(content, fsenc) try: return bytes_( substitute_double_braces(content, TypeMapper(vars)), fsenc) except Exception as e: _add_except(e, ' in file %s' % filename) raise def module_dir(self): mod = sys.modules[self.__class__.__module__] return os.path.dirname(mod.__file__) def template_dir(self): """ Return the template directory of the scaffold. By default, it returns the value of ``os.path.join(self.module_dir(), self._template_dir)`` (``self.module_dir()`` returns the module in which your subclass has been defined). If ``self._template_dir`` is a tuple this method just returns the value instead of trying to construct a path. If _template_dir is a tuple, it should be a 2-element tuple: ``(package_name, package_relative_path)``.""" assert self._template_dir is not None, ( "Template %r didn't set _template_dir" % self) if isinstance(self._template_dir, tuple): return self._template_dir else: return os.path.join(self.module_dir(), self._template_dir) def run(self, command, output_dir, vars): self.pre(command, output_dir, vars) self.write_files(command, output_dir, vars) self.post(command, output_dir, vars) def pre(self, command, output_dir, vars): # pragma: no cover """ Called before template is applied. """ pass def post(self, command, output_dir, vars): # pragma: no cover """ Called after template is applied. """ pass def write_files(self, command, output_dir, vars): template_dir = self.template_dir() if not self.exists(output_dir): self.out("Creating directory %s" % output_dir) if not command.options.simulate: # Don't let copydir create this top-level directory, # since copydir will svn add it sometimes: self.makedirs(output_dir) self.copydir.copy_dir( template_dir, output_dir, vars, verbosity=command.verbosity, simulate=command.options.simulate, interactive=command.options.interactive, overwrite=command.options.overwrite, indent=1, template_renderer=self.render_template, ) def makedirs(self, dir): # pragma: no cover return os.makedirs(dir) def exists(self, path): # pragma: no cover return os.path.exists(path) def out(self, msg): # pragma: no cover print(msg) # hair for exit with usage when paster create is used under 1.3 instead # of pcreate for extension scaffolds which need to support multiple # versions of pyramid; the check_vars method is called by pastescript # only as the result of "paster create"; pyramid doesn't use it. the # required_templates tuple is required to allow it to get as far as # calling check_vars. required_templates = () def check_vars(self, vars, other): raise RuntimeError( 'Under Pyramid 1.3, you should use the "pcreate" command rather ' 'than "paster create"') class TypeMapper(dict): def __getitem__(self, item): options = item.split('|') for op in options[:-1]: try: value = eval_with_catch(op, dict(self.items())) break except (NameError, KeyError): pass else: value = eval(options[-1], dict(self.items())) if value is None: return '' else: return str(value) def eval_with_catch(expr, vars): try: return eval(expr, vars) except Exception as e: _add_except(e, 'in expression %r' % expr) raise double_brace_pattern = re.compile(r'{{(?P.*?)}}') def substitute_double_braces(content, values): def double_bracerepl(match): value = match.group('braced').strip() return values[value] return double_brace_pattern.sub(double_bracerepl, content) def _add_except(exc, info): # pragma: no cover if not hasattr(exc, 'args') or exc.args is None: return args = list(exc.args) if args: args[0] += ' ' + info else: args = [info] exc.args = tuple(args) return pyramid-1.4.5/pyramid/scaffolds/alchemy/0000775000175000017500000000000012210157153017512 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/alchemy/CHANGES.txt_tmpl0000664000175000017500000000003412203712502022351 0ustar takakitakaki0.0 --- - Initial version pyramid-1.4.5/pyramid/scaffolds/alchemy/setup.py_tmpl0000664000175000017500000000221312210154276022261 0ustar takakitakakiimport os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, 'README.txt')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() requires = [ 'pyramid', 'SQLAlchemy', 'transaction', 'pyramid_tm', 'pyramid_debugtoolbar', 'zope.sqlalchemy', 'waitress', ] setup(name='{{project}}', version='0.0', description='{{project}}', long_description=README + '\n\n' + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author='', author_email='', url='', keywords='web wsgi bfg pylons pyramid', packages=find_packages(), include_package_data=True, zip_safe=False, test_suite='{{package}}', install_requires=requires, entry_points="""\ [paste.app_factory] main = {{package}}:main [console_scripts] initialize_{{project}}_db = {{package}}.scripts.initializedb:main """, ) pyramid-1.4.5/pyramid/scaffolds/alchemy/README.txt_tmpl0000664000175000017500000000035712210154276022254 0ustar takakitakaki{{project}} README ================== Getting Started --------------- - cd - $venv/bin/python setup.py develop - $venv/bin/initialize_{{project}}_db development.ini - $venv/bin/pserve development.ini pyramid-1.4.5/pyramid/scaffolds/alchemy/MANIFEST.in_tmpl0000664000175000017500000000020612203711415022301 0ustar takakitakakiinclude *.txt *.ini *.cfg *.rst recursive-include {{package}} *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml pyramid-1.4.5/pyramid/scaffolds/alchemy/production.ini_tmpl0000664000175000017500000000232112203712502023430 0ustar takakitakaki### # app configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html ### [app:main] use = egg:{{project}} pyramid.reload_templates = false pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, {{package_logger}}, sqlalchemy [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console [logger_{{package_logger}}] level = WARN handlers = qualname = {{package}} [logger_sqlalchemy] level = WARN handlers = qualname = sqlalchemy.engine # "level = INFO" logs SQL queries. # "level = DEBUG" logs SQL queries and results. # "level = WARN" logs neither. (Recommended for production systems.) [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s pyramid-1.4.5/pyramid/scaffolds/alchemy/setup.cfg_tmpl0000664000175000017500000000103512203711415022365 0ustar takakitakaki[nosetests] match=^test nocapture=1 cover-package={{package}} with-coverage=1 cover-erase=1 [compile_catalog] directory = {{package}}/locale domain = {{project}} statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = {{package}}/locale/{{project}}.pot width = 80 [init_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale [update_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale previous = true pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/0000775000175000017500000000000012210157153021233 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/views.py_tmpl0000664000175000017500000000210612203712502023772 0ustar takakitakakifrom pyramid.response import Response from pyramid.view import view_config from sqlalchemy.exc import DBAPIError from .models import ( DBSession, MyModel, ) @view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): try: one = DBSession.query(MyModel).filter(MyModel.name == 'one').first() except DBAPIError: return Response(conn_err_msg, content_type='text/plain', status_int=500) return {'one': one, 'project': '{{project}}'} conn_err_msg = """\ Pyramid is having a problem using your SQL database. The problem might be caused by one of the following things: 1. You may need to run the "initialize_{{project}}_db" script to initialize your database tables. Check your virtual environment's "bin" directory for this script and try to run it. 2. Your database server may not be running. Check that the database server referred to by the "sqlalchemy.url" setting in your "development.ini" file is running. After you fix the problem, please restart the Pyramid application to try it again. """ pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl0000664000175000017500000000156212210154276024012 0ustar takakitakakiimport unittest import transaction from pyramid import testing from .models import DBSession class TestMyView(unittest.TestCase): def setUp(self): self.config = testing.setUp() from sqlalchemy import create_engine engine = create_engine('sqlite://') from .models import ( Base, MyModel, ) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: model = MyModel(name='one', value=55) DBSession.add(model) def tearDown(self): DBSession.remove() testing.tearDown() def test_it(self): from .views import my_view request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['one'].name, 'one') self.assertEqual(info['project'], '{{project}}') pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/0000775000175000017500000000000012210157153022522 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/headerbg.png0000664000175000017500000000031312203711415024765 0ustar takakitakaki‰PNG  IHDR4b·Ît’IDAT8í“AÄ C“\«—›SÏn`b‹íºà)4ÁoŸàû9DRp‘hµÈ¯«×uìô½?÷Þ²‡ ”@»÷¬\SÍ=ýÌ®Š3}½÷­¾ÚOÜÕÜåo¼±FŸ5´9,×cå©ïß»'çãmZó&kÌéä… ´q~øx²Xò5†èßE3˜,óçû÷”01]îsÖIEND®B`‚pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/pylons.css0000664000175000017500000001221112203712502024552 0ustar takakitakakihtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-size: 100%; /* 16px */ vertical-align: baseline; background: transparent; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } :focus { outline: 0; } ins { text-decoration: none; } del { text-decoration: line-through; } table { border-collapse: collapse; border-spacing: 0; } sub { vertical-align: sub; font-size: smaller; line-height: normal; } sup { vertical-align: super; font-size: smaller; line-height: normal; } ul, menu, dir { display: block; list-style-type: disc; margin: 1em 0; padding-left: 40px; } ol { display: block; list-style-type: decimal-leading-zero; margin: 1em 0; padding-left: 40px; } li { display: list-item; } ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl { margin-top: 0; margin-bottom: 0; } ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir { list-style-type: circle; } ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir { list-style-type: square; } .hidden { display: none; } p { line-height: 1.5em; } h1 { font-size: 1.75em; line-height: 1.7em; font-family: helvetica, verdana; } h2 { font-size: 1.5em; line-height: 1.7em; font-family: helvetica, verdana; } h3 { font-size: 1.25em; line-height: 1.7em; font-family: helvetica, verdana; } h4 { font-size: 1em; line-height: 1.7em; font-family: helvetica, verdana; } html, body { width: 100%; height: 100%; } body { margin: 0; padding: 0; background-color: #fff; position: relative; font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif; } a { color: #1b61d6; text-decoration: none; } a:hover { color: #e88f00; text-decoration: underline; } body h1, body h2, body h3, body h4, body h5, body h6 { font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif; font-weight: 400; color: #373839; font-style: normal; } #wrap { min-height: 100%; } #header, #footer { width: 100%; color: #fff; height: 40px; position: absolute; text-align: center; line-height: 40px; overflow: hidden; font-size: 12px; vertical-align: middle; } #header { background: #000; top: 0; font-size: 14px; } #footer { bottom: 0; background: #000 url(footerbg.png) repeat-x 0 top; position: relative; margin-top: -40px; clear: both; } .header, .footer { width: 750px; margin-right: auto; margin-left: auto; } .wrapper { width: 100%; } #top, #top-small, #bottom { width: 100%; } #top { color: #000; height: 230px; background: #fff url(headerbg.png) repeat-x 0 top; position: relative; } #top-small { color: #000; height: 60px; background: #fff url(headerbg.png) repeat-x 0 top; position: relative; } #bottom { color: #222; background-color: #fff; } .top, .top-small, .middle, .bottom { width: 750px; margin-right: auto; margin-left: auto; } .top { padding-top: 40px; } .top-small { padding-top: 10px; } #middle { width: 100%; height: 100px; background: url(middlebg.png) repeat-x; border-top: 2px solid #fff; border-bottom: 2px solid #b2b2b2; } .app-welcome { margin-top: 25px; } .app-name { color: #000; font-weight: 700; } .bottom { padding-top: 50px; } #left { width: 350px; float: left; padding-right: 25px; } #right { width: 350px; float: right; padding-left: 25px; } .align-left { text-align: left; } .align-right { text-align: right; } .align-center { text-align: center; } ul.links { margin: 0; padding: 0; } ul.links li { list-style-type: none; font-size: 14px; } form { border-style: none; } fieldset { border-style: none; } input { color: #222; border: 1px solid #ccc; font-family: sans-serif; font-size: 12px; line-height: 16px; } input[type=text], input[type=password] { width: 205px; } input[type=submit] { background-color: #ddd; font-weight: 700; } /*Opera Fix*/ body:before { content: ""; height: 100%; float: left; width: 0; margin-top: -32767px; } pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/pyramid.png0000664000175000017500000010043712203711415024701 0ustar takakitakaki‰PNG  IHDRî©t߸7 IDATxœì½wœÅ÷ÿ®ê ›µ «œ³–@QÂ`‚cls€qÀÙÆáî±§ó%ßó»óÝÙ>?ççüpNgŸ³}‡98°M8L2QH("i…ÊÚ4©»~|»gzf'íjvV»ª·^­éé®®ªîéíþÔ·¾õ-…eÄsÍ5×Ä»ºô´X“·H}®vÔ03ŒaÐ8Üõ;Õ1¥Q ”:f0¯*Ôv0ëL&ù|&“ÙýðÃî:Z,‹Åb±¨á®€ep\sÍ5ñLÆ™éjo¥6ꔺ˜¦E)eדÀƒR*‰1{ æicxÈdÜ?x^r‹ñ‹Åb±X† +ðFW]uÕ—èÕà]‹Qg+­æ*¥0Æ`Œîê*”R(¥0€qÝÃF™Ê¨ß×üìw¿»oãp×Ïb±X,Ëé…î#„s®»®i\Æ»ÁxÜœ¥§Àó¼a®Ùéˆxç¹cÌK(õË„›üÖc¿ýmçp×Íb±X,Ëéî§8«W¯ŽhÝx)Žú¸Vêu(ñY·Öõá!ðB2Æx 6»˜¯¶6œøþÝw?Ö5ÌU³X,‹Å2Êq†»–Ò¬^½z‚ŽÄ?­õJ©å@t¸ëdÉ¢”R ®N¥âsæÍ]°ãå—·íîJY,‹Åb½X‹û)ÊåW]{™ç¹Ÿ×J_ ÖÂ~*£”Æo†/Ä£|ã¾ûîKw,‹Åb±Œ>¬ÅýdõåW¾¯i­—yžgEû)Ž1­u›1fUÊõÚçÌšñøÎ;SÃ]/‹Åb±X,£ +ÜO-Ôe—_y‡VúËÚÑSìÀÓ‘ƒB2¦Ñ磜ö)“ç?ùÊ+Ûû†»^‹Åb±XFV¸ŸB\ºúÊÛ´V_QJµ+ÚGÆF)¥Î‹D¼‰³gÍ|dçΉᮖÅb±X,–Ñî§«/¿ò=Zó”cŒí#ƒAiçlƒš6cÚ’‡;;·Zñn±X,‹å¤±Âý`Õå—¿ÞxêkJéñÖŸ}´`PJŸ‰“Ò»vìøÝpׯb±X,ËÈÇ ÷aæÂÕWž©1wi­çZÑ>ºP œ5{Îìõ»vìØ2Üõ±X,‹Å2²±Â}¹æškân&óeí8WXÑ>:QJÇ ,;{ÖC;wî<<Üõ±X,‹Å2r±Â}™2}æÛµŠ|Òã` ì2Êc ZéIž¡cL[ë}Hc±X,‹Å2¬p&.½ôÊÀÿUZMµÖöÑü¾j~C,þRgç·»>‹Åb±XF&V¸jÆœ9wjÍ[l™Ó¥TÔY3§ÿº³³³w¸ëc±X,‹eäa…û0pÁ«kG}k­í§:Pú@ç®k‡».‹Åb±XFz¸+p:â8úv0sŒgýÚO—Åx­u#F½ó /œˆÅb±X,ˉ wN7Î;ïÒ9F¹×+ ÖÚ~:áyƘ3ŽÜ3Üõ±X,‹Å2²°÷:ãÄôÐs‡Ûl—ú/¾Õ=¦áº .¸ ‹Åb±X,–`-îuäœsΉ‚¹\iÝàyvPêéˆg<<¸(v¦/w},‹Åb±Œ¬p¯#±ÖÖY&mÎ6žÁJ=M1 `q$9+Ü-‹Åb± ë*SO2™ù`¦ZßöÑRªä‚Œw.òÍb±X,‹¥*¬Å½~hãéåJ«˜±n2#¥ªÐÚUõ¦˜×N9çœÆ}O?mcº[,‹Åb© +ÜëÄ9çœÓf±RϺɜҔç5ùåŒÁÀ¢¹ öAg-²´X,‹Å2ú±Â½N8ŽÓh0óŒ„îêŒZª²ˆW`0¿Ï@ÊõD¸Mg>V¸[,‹¥z•Ý,ƒxf–QˆîuÂÇc‘ ãÍiìÕìyiâÑÚ ­¨…P¯w™J)0F5ª’Åb±XF?qàÓÀ" Sd¿ú€ï¿`Þ¯ó¸þ§D€µÀ[€v`2I˜ Ü4Ow°\˱½N脊ÇmÃèÓÒâîy†hD3kj;^éAk(¥"Œ{-OF|ŸìïgŒÁ£=ÏL:©Œ,‹År:Öç•IÓˆö ÷‹€w#Öüqˆ`?‚ˆó€ÃÀç€Và àI`°øß~ßÁ ÷!ÇF•©™hFL#Æ ûD@ñôö¦¸ìâ¥üí'°pZо¤óùûUè_­ËVEþ ×u•­•blÿ;Åb±X,–¢ Q!MH "ïnÞ ¼„ö?ó·ýÐÊw)0iHœÊ#9ˆr-ÄZÜëD4ÑFeN·PJ)zúR,œ7‘¿ï ¦6üžO¿µ—5Fw/4DG¼ê¯ËÀ­åµ½æƒ±Öçc£”ñâ5«Ô©ƒÆ„–fÿ³Õ_oð—ÒíÆCü½ÀqàUàpØOñ®a‹ÅbLC\;3ºÇUzUã_Œ]þŽ"ÏãuÀ3þþK{ÛnÄ"¾¿~>2'ÉäA–k V¸×cÔh’íÕØD2Ø–>üîULÔBú•$ËôqûU­|õšT"N}ÜcŠQùÂ2|W £âv˜\̦ûßÇcýÏ&äeñ?µ¿”z¹xþâ"Ö>D´ï6 /’uˆ˜ŒUÉb±Œ¦ â5À,ä9sqù69Ñi©ž8ò¬Vô7®8À&äº_ <,~…üV¸×+ÜëÈHWiýh…ôžgȸ.·ß²’+VÍ'ÓÛƒç¼T”[¯H²}_#¿|TÑÚ8tu†…w¬FÈô˜pzE¾÷Ì(àMH7jóäÝŒ4¦+€‹ü^à!àgÈ‹ùÕ!(Ûb±œÚLA|ª¯,Ø>X\¼ Ti© bHy¸xòZ[ ¼~ëuZa}ÜëÉ)àk^/¿p¥}‰4¯¿d!7ßxn2çfC&1;nH°t–!xÅL)^ç¢õ¦túRªºTÞÅòLÉÅ–¼óÄü¥^Äëþ»+Ï¿!7;Ð×b9}ˆwÒ_´‡™ü-"ä-µÁAzAŸ&ïvÛ±Ööºa…{ÝH`ð0fx–*áÁ¥ ½})Î:c Ÿúèe´D!“L ÞH&`zGš?gŠŽvH¥ â9RMþô_ ?]±4¡´yé•Ô¹ßB°0 z•B)åï—ë‹7*\¶‡sà8Ò=ûMà׈EÞb±Œ~Î>TEºËç‚•µA#Ñfžzñ¾qOr†±^§V¸2Dö_{ì`èKdhiˆòÇﻘÉ›I§’(<0AhXJ“LDX>?Í7¸¸.d2¾¨.q…u*›.ï¼*,’ªÌR¹NZëŠi¤î³Œ¥†Dù¿´ ou,Ës)2Ƚ X‰u ¹^1òÅxÓ} ° 2öÈøûFcÀ…S{3ב¡ˆ(Óßï¼ú2NæØRxžÁó<Þú¦³8ï¬idzXîÏ€Ñ~yIi®½(Åã¹ï)C¤Qöôk2iDÖ7—$_‡S©ý¼07É£xcÅøÿ—lÌTÓÆñ{‚ÿ­|¯9mHø²iÀg_L‹Å2ú˜<€´“A™¢ºŒ6’À/w˜½¡í¯_žÿ,þ ñÿ<[ךž¦Xá^O©ÔÊZ¾KäY•µ|€õ©”§RоdŠË/šÇm´/Õ‡çy!˜àx%"±²Çã†;Þ˜b×[_æF¨8¦H]_w¥Šïƒ*¹¿|™¥÷—>°_(È~ô£ƒLæqèBb§—ip%ÄŠÖŠ„w›ÈÀ{p»Ÿ×Hw®Åb]ô m6”ì@è¾XdûVà#¡ï_-Øÿ§œnoµa ÷:‘œ"j´ª(&¡ãªu_HÈÄjy%´Vôö¦9cÁD>ó‘ פI%RAe±îúî2ûûV$ó¦»üÙÛ2|ükQNôb‘œx/W~©}•Ï©øõ)%èË)úbe•®—|c†Õ9¼Îüyf/ò &òH“ÿBÕH÷lb=Ÿœt‹Ÿ‡ˆùjPÈ ©ÈL6l¤Å2ºH¤˜g±Õ+Úë„îu¤˜»EwJ¡OÂÆä—WÓßU$X/Öû›+¥H&3Œçãï?—É“šHu'AûÖåÀÊL•jÊ„\ç”G²OqöB¾þþÇÏí¦ýz;„ZB$|7áíácŒÉ}/8çR×£’0?ÃÂîjðŒw: —Úü÷Ò ÷Ò=» ívÕGù2ˆê‡(Ûb±œúüx ¸¨BºÀ½XQiEØÁ©õ¤H0’“ ¹X.Ÿbyi¥Kî×JݯPÙ}á%||&í’q=>𶜿b ™Þ¤¸Çxžø·>ãùϼ¡£"â=M&i¸ñ’7^ ½ q§ —_x~ŽÒhÊH“· 0Uáº䥵ӯ Gk­ÑJe— 7­šÐä-¹¡pP8h%Ÿ*(³ðÚ>&÷“1àbaûðFànªëúŽŸB&f±X,£‡ƒÀ_P~‡n$äsu©‘ÅR'¬p&*Em f׬tü@"­”KS*oŒÉ-Åê $’.—ž;ƒ¯šI¥ð<L ÿè/›Œ ¬ðâæn”ñH{Š˜V¼÷ê ¦+™ü(1ýb½” SM„—ÜR,ÒLÁ¿øVJçÊ G“ñ×µ–%»ˆ¿eÐàIà½Àÿ¡ºÁfKK½Åb]<|qÅ;‚¸Ã$£ÀSHœ÷ïr:™H,§ÖU¦^$À89÷‹b®aÂn(åŒÿwv¿$ª~ß—H³lñD>sÇy4Ç • ´TN çLû®o€Ñ„Ý̕ր"•2Lé0Üy³Ç_~'ÂñC,šßn´ämÔºð<2ø´ûUä×/ûl)EvlRJgËö<‰°cŒ¡„›½¥2Kº |œòÏ1î¿B–Y,–ÑÃOwºyHÏš:]À¡a¬—Å2dXá^WLö3'ªË„¤|”“ʾÝʰè Êêó.Ü–L¹Œimàß³œéÓ›Hu§D˜~íá:d¨Ê&±Z‡2 ŽQN:\p¦Ë{Ö8|é?žHÞÔÙQ«ë\í¹ôObòEy òV(PA#!eÇ2\$fû<àÍÒ.®Ã w‹e4r ]øôpWÄb©ÖU¦Žä{äœ> ·«"ûú;Š”ö‡Ïù™ûSéþÇÙ–u)²=ŒÄk‡÷Ü|ç/ŸHª' x~eƒŠø“.eÝf‚™XµÔËàûËhäVÔ`4Æ7¥¸ñ¢4×®„¾¤øŽcü¥œÜ1h±Â‡|] ýñå¼Âî1¾‡zÖ¯]ƒ’|%oGòÒ¹¼ƒ‰–ÂKá€B—Yü<ý¼‚ßÔößž4ÝÀ(ïç bu_Mu¶X,‹ÅrÊb-îu$ì“s…éŸN…þï·ï¤¬Ì…å”4·—È+U%™ryÛ ¹åºyd Œçf9*M†`=ˆ#ªÕ®B1rüI™|ÿ2ˆD ¹Áãð±k7šÊœÅël ¾÷?§ð)«Ð9—¬?ð—ÉsÛQá¯è¢å„\¤‚ØBÆP*$¥e@üø7Äu¦KéÀ¶!¯‘Åb±X,C„îu#A0{°…ó‚æa(çÂRé»êç…HGUê 7ŽPB¥(¬b v»{3,š;–w¼e±ˆ!ðÐYŸö°XŸQÐuÍŒ\‡)¨“lO»0¾ÍðÁë]¶îuèê…x,—.ïS0& ÿ#”6×,!ÚÚOt wò¾ãl ûßk•/äà ­<<¿C^ Àr2ü'ðAd*îRt3±ÂÝb±X,#+܇œu·: zU~Ô…_Šì¯lu®à÷­©´Ç¸1 ÜñŽ%tŒ“î ÏkQ*ŽúW™¬…BŠØÏE!iP$“pÆxç•ÿï×"’#¡»¶”¥½ÔIî/tv ê×#Q&¯jz8Ä•¦_2Ëɳx¸¬LšÄþÁºÔÈb±X,–!À ÷:ˆZcrQPªî%¾©B³zx›*bËWR4äÆÑ/¿Âº¨À_ñ®7/àÒ×ú~íO{6«‚A©aK|Ð@ØÚNÖZžmÌ„º<™¤âÆ‹]vîpÏZˆFB5ÍZØs¶óP»¨È9…NÈÿPy×4\­°;S~š~ü‚4á²åtZñîKm8Š <-'ÜA\edÀ…Åb±X,#+ÜëDˆdµ¬ hŠ˜ÊýµBx“b2?ýõhQ”Î;¯ΛÂF„¡;áò–+gqËu3É$Ò¾?{vDlá®(ˆÿîùb8‚2:\P¿:øUE¡H»šxÔðÁë ûFxv47–䯄s,s] …{‘ߣŸp/Ù3Q覓ÿ]+…1^nÀ±µ¾×Š$þ­íXá^kÚ€9H(¾qH‹¼™õöÅ–Z)Àä·l%7à8 Gâyö#wZ€ÀT`¼ÿ]=ȹîvSݼe âb6ù}›ýí=È5î^RCPöh@#×mòû‘er¿îö’›1Úb)‹îuED¬vræðBw‹@ær”ý¡õ57œO^~…ŽÞÁÖâ:9›°_£@)ziÎ^2ŽÛß2›ˆ2¤\7dåœÀC"ݘ|!o‚m"ÜÊÅwT&Ü=*׈…Z)He ½Þ·þúǺ!í=ŠŸ²Ê¥)lÏ”s)Õ{Q˜¦Ê<ƒßW: ÂMZN–jDZ#å›K³€³(/€¢ˆP©Wè¹82­{ ¥g‹""jÕ+z "¶ ó ºÂ6/WÈc)p5°˜ø?7ÿ} ¾‘¤±¸88˜ŒUhD®KŒ`àœw0O°™„çidҮ݃¬Ç`˜ã×7°l„‰;*äÑ œ \ \èçÙŒœ{ðäK½HƒåYÄ ìNþ\Û•H$¦ó‘FC3òû+ûàà7Ôw ÉL`9ů3ȵ~©_53-× …üM¬.@ê8ùŽ“Ó]idð[Ð[ <<y‹¥(V¸×‰8É›0Hû‘Eòr1#4ôwù(eÏϯ=Љÿb;Ãù¤S†ö–8yÛ|&uÄIõ¦é7*3‹ µ&Â0Uv]a Ux»’%¢]‘?@V‘JÁÒ9†w__þ…Âõ  &³R¥Ï-lU/ÚK¡ wCjlå‰õÂtyç婵Î^3ãyYWKM¨Å‹yð¯ˆ-dzÀõÔÇBödöÇñeÒ(àKH„j¸øP™ýÿf³˜èž¼ ¸ NÅÞ!ÍÈ5Ì > X\ƒˆö¹ˆXuÊäÓZ_ˆ§$°¸ø6°uu(W_¦ôùÿòkd5—FÄs åÃ6·#–Ü%À[€—€ïßB⛄&à*¿ìóÈYö«)ûMÈX“Ÿw! §¡f rËñ ä|Ž}uˆ# ¶w ×qÒ¸¬†yˆÈÿ°¸ø&Ò£a±äa…û0 xà¹ï¡=%Ü. ˜5¸ÿäMù‘RBéµÊêcÝ23®°0/Ï3(m¸yÍL–-Cº/«]!ŸFl#”ÎOƒBár1äÃç›s2¾ˆ[°‘0‘¯;žÝá¡õ2¸T²*g9_ÎP'Ô¦(7¨U[è^ÓßÝ&´O4äŽw¬X"ªy–õQ>”Ï3ˆø½¶B>ËeÀÃUÕlð(Äâ9½Bº£ˆµ³ZëvÃ¥h*±}%ðUàµU”1PKû"亿±æW#Ô+¡+ñ2y#ðÏÀZ!ׇܥD[«¿¿Ð½dðYàfD”ro.Gziþ‚êÝ•"!UßNΪ>¢~Ÿ^ürO%šò÷1 î\ÃàcHƒ¶cy÷kð^4ÈônX,€îu#™í;¹+òq§¼•\)•5l‹ø.ÜALøÜvSDˆëP>Z+L­H¥\®»l*o½~:™dσ¼€‡…âÜ„…z˜@Ü#JÛøe«®r'ï[ã¥- rãYQdÒâ"óþkáh¯ÃK†Æ¸œ[~ýK\ÓbÖõ¢»_C©H>ÉKÿu•©1íU¤é¡¼+I/ðÄbZî…ߌÌÄúbÑ*Z€7T‘îà¹äÛëRâ8†ˆ¢°ø~b‰Ÿ2€rªa1ð~àFĽ`(Y | ¹¦ŸEzN†‚.D”—î þ¾°p¿½ójT‡ñ÷ Äm©ïEDûü•}!byÿà+HCf(¨fxÿP‡ˆ#O ÷W-9 é%Zü9°½Æù[F(V¸×‹8]«PhÇw›(ŒôB¡°TäŒó*d”»n@RQ6…ÖKä£Ã¯Á±b´·ÏãŒym¼ëMÓˆG©„ñ³ ? MpR²^èçžÅC&\R`™4ä“·.£p³>î’Wîd“?Þõzøûÿp8Þ±Hp¯ ¬ûù¦—ì‘u óH^Y÷¨à\mT™ZCºì+qÊ–àGwƒeÒ]ƒXŸwUQî`Y\\!þ› Ä주p“ßLý#D„M@Õòzà£Ô÷]t5âztCã:ÓGy×­Frç«€·".ck\¹ˆpNÿUd¿|ø;*»‡ ”vÄâþšÑù´kBÎíä»iÕ’(p+0qÁ98DåXFå|ç,5ÆÃ`ð²®ÙTC‹ÒÁ¢ý%¼M¬åÚ_—ƒ*û©•Ê?ÆÑÙ|t(_í„Öµ–cu.G+Ò76ÆûnžÍ„ñqR‰‚÷P Ô!_ßTî7P”?ƒªò…¸XÓ5íïÑ~ÃÁ -‘кïB£‚t¥’ÉKf)n]¥¥¤´ãŸÖ8y‹“[YtDg×Ç?6X‚<'o½09® ¯Hñ¼Âùˆn7T7ŽÐRc¨lír‘]I@ì~^E™Õˆê“e ¥ÝV:Ëÿ@„Q‚ò7_œÜ»aC'Úgðbädš¿ç"¢¶šß@ F¥ûìßÀЈö€YÀß#">Œ|ø"µí ˆ%ú†!Ê8|ø8C'ÚÃ\ŽüŽÇFð9í±÷a@œ îY =þݹM…Vä"2ûùcæÐz¸Œ"Vdc@+w¼a+–´és}—–{GÞë2ô%ÏâÒA9JçüÜ Î7üÝøV÷l}³ >5é”áuËa×a‡ûŸ†˜“;ŸR1é gV ׯŸ›‹É?6›¯êß;dh0¾Êž‡¢>–š2ÈXŽ.ª³Ž{ˆû㔟‰5‚XÝÊЄákD¬Ñ•XËÀ#y¤(/ÜüýmÀ'ñ7T¼€D~¹®Lš^DÜF¢mD~Ï>䯘ŒˆÓiTßÈX|øSj+†ª½¾K16±B~.âæÒè{{)r?‚œk×­ˆz%ñ 9ÿêpD›jiA¬Ò/!ƒ.GÍÀß ƒ^jüìFƤô Ïr]ÇRþyò›mÂZ|N{¬p¯É$FGDü*…ÖN^T™¢~îaÑWÌwºˆKFéÈ(ýdVp äìKz\qa×®êÀM{¾{GÈ&åZÉÖ¥´ˆwå¢ *šuÏ),_ˆ*óÖKᕃŠ-{ñþçž=ß‚khB' ³îBábJ¸Ïôû-rAÙƒÁÁ…3²¢š\Æ<«ãkÞLåaTéâyàQÊ‹Ia½X_e¾á"$¢L9º€Ÿ0ð •„eâ~ô6ÄMf0T+ðÒH”•kÉoN'küoÿýÀ!Dð$é_ÿ/{6b|Ò+R‰÷#á"Xe}«!IåÆÜdÄÒ¾ Äþ>äüÄç°¿},¹s\M.Ž}%Þ ýO¤·á‹”Ò ¼~ ñ«¢'1Ý/E VšË1ïfäÏ¡þ7âÞU-.r=ïAB­îAîã¢Áš‘hJ¯E&‘{ÅTihÖ3´¥åÄ ÷zâ¿jÄ墿_¹¿Á—¥aËp¾P,&γ‚1ä…ž‹oÄÒݯ(ÕOàö%]Îjåí×O!¢錗«C1¡®ÈùŸ‡lnx¢©@¤‡Ý`ŸÛC‘räú¨lViWÑÒ7¯R|í¿4=IñwÏ»fAzÿøÂk×½°N½e"Ø„¿ço–HB&¤k'`ª3‘(•ØHõ.IÄêþ:Ê»ªL®`h„ûeTvaØ <4ˆ¼3”ov;ˆÕúC”à‘(<ë‘ØáI?ßvÄ2^­ËH¼í~žÿ…éøÖÕBÜœö#½w#ÂøŠ ÇÅñþÒ0¨Ê ÔàN$ìe!i°ü‡¾Ô=û]$|à'!]‰Fdêï?A„b! þïÆëá"i‚²—ŒD>©&Л–T‘öTæíÈüÕ`ëý‘ó.w/oòÓ| Åù$¤d¡E¿Ëiîu#Ž Þ—Êà8N¿8îiSn›1~˜ðþÐ1yQdò ç-žç¾ß¹¿MkE*mho‹óî7MaRGŒt_&Ôp𠕨?øT“×Þï5]ä½m4Ç·¼‡-íás ¹È9„\WŒRþÌ« ”G2©X:Þ|‘æ‡ÿ#þÿŽ"/Nzî܃ÿó¯eéž r=-ðá(?¹|ƒ1ºNÄÁó{ <ãáXå~²hà*GÃèCÉ@¬}Dâ2—ãFàëä\jÁÄšZ©þn¤Û} ³X‡q ø%öïB¢ïü 7XÌ‚tEUÃ.¤2ñH&ؾ IDATþ'ç~dÆÄíˆeùfÊ7“ÏAÜž¾we†IQ>ÚÐ<äž-ü}{€BBVVjdD~ÿ'——RÙp1Ò˜)Ö“t‰Ý•ï©£ˆùH#àÏ©¯¼i =ÆÈµ/@¢ïT#ž»þ…Ò  bBæx ¹¶ÌаŒP¬p¯I0"Dµ?ÀÑ/_<‚„| X˜Ã‰| Ý;²~Ù*°i üh‹9K}n¿á-Wv°|q3©„KÎ $0‡ý×ýÊ„© Y÷p÷àðª’ö@øT©u9$å*.=vrx|“GC,×ó­cž•»xÏEá¾|W¦KL1×¥ÜáE÷9ÚÁhWœŒÁúÊœ47ï©"ÝFÄ‚>¶"⤒p_ \2ˆü˱‚Ê‘C5.3ÌB)Öpø9"HÖVÈc † 1ðÿ­{ׂ¹ˆ³-ˆEøÇÔf¼B¥A³Å,ÔG‘(,ÿRáØB^þ çq}…´íˆ Q!‡ëô­” 9q:i8Tjh^‚4’*Ý;§"1ÄÒ^MÈÌcÀ_"Q§;xú826`'ÒøªÁá–ˆîu$ø v´Æ‰8㔥ü²+…< §)6@³XxCô$ ׯjçÚKÇâe¼")Mþy+aaø°÷·)ß¿]7€v0ALJcÀ¸~Z1Š*W¢®dõrè]è…ªúÖpOi"ŽæÍçkŽðâ+Š–¥ŽÁ‰HD9Ôàe; DDëý(u .}y×™Báî_S椹±LŽ«.ƒXR3 ü‘P}åʇXÝk)¢×PÙgÿ!ªŸ\g  …i€ÿ‡X»k\žap¿O5ìB¬ø?¥¼ÛÓ Ä¾©Få¤w§±®þû Ë:Šø\Ϧò¸ˆbei´ †$Òp˜ƒôZ”c*2.d$ ÷+°•èþð•û]dpðW)îÞd9 ±Â½Ž?ÊŠˆÉ2ƒS ¬áEÅvÁ¾âô|«2…ÇøŸÉ¤aÁÌ8o¸|,G‘._!$ÒûÙŠ øÀbX»=ŒIC¦“8‚!&n¼„¬{iÀÅÆx.ö­Óžˆþ@ê*oþœF)’hÚšàª9Üý›&N$4-M'B,%LjÇâòo ÓFú]» êÉ6ò¯}±k¾Î…ÛGãyâ cŒ±Â}phD(ÿ-•géÎìÀÃ? þוfR½%{YN˜ Èl©åºc\ÄMb°ÛT YŒ!VÙZ‹öz ò¼ºLšéÈÌ¥µî.‹RóÄõèdØ…4dÿ•E:ù ð³“,ûby¿é½(…ƒÜÛ È=8RhF& «4æÄ ûû5.ÿçÈýùåçk¡Xá^gŒ18‡ˆ Í^šo9û¬çìØÅEwq—BÇ—™ZC*cÚ4o]3qcâ¤.yÖóìá`çQ ØMZ¸Ic¼Æë7ñúÀK`L÷È>¼#{}c¹KÖ0¥²sÂb?+šÐá“Úw©ñ­øAµ ôuiΜò*o=<óóÇpœP½ý뢵&‰à8±XŒææfš›šhnn¢¹¹…ÖÖ¢Ñ(±XŒx<ê­ñÆóü߯¼>|͵Öx‘‚ –0 é’¿ƒêbOw#~»ƒžFDÿ5”Có³©p_€„ ,ÇzDˆ—uá?„…ì:‰2‡“È€×rÂ]#.‘O6#÷h-ÊüOäïãœ*ÓoGÜ0jQöïßìJQˆæ"½ICÕÃ2œ‹ ­Ä}Àÿ¡ö>üby b$°œæXá^OŒ¸eh¥ˆDÒ‰&SiÒ¥ÿöD9áVw%H¬ñjCižª¼€ôP”‹[¾±¯Käýµ›½õ0"«î?Eâ«×‚4Ò蹑ò‘ˆ&"½S#E¸+äœ*M²Ô‡ŒO¨6 Ò@9|yþUнoåXá^'€ãûRƒSµ ¢½äÒåù¨‡|¹Uh)ßöÂÏ¢þÙ!7õ¾\öÚf®¾¸ 7í[¯M¯=ün×`\ŒÛ ™pbÜ^p»1^JºñKdõ«¦Ÿ!Ç(ü^TDD¸/ÜsñÛ ÂOªp¯„ç÷ÄBo 0n˜¬å=M&­ˆE ñæcì>4†'¶â½Äœ©Ê]±ÞÞ^zzz0æUœH„X4J<§µµ•¶¶6ÆËøñãioo§¹© ”4ÀLv0l~ŠXÜ# ž18§nçlÄ·y±'7©K˜bIŸŠø/Cb/CÂ>„ï#–Ì“l¸q'('ÜAüÒïâäDØ8à ÒA„ûPLúTŒß#bp¤„ŠœS&Í,¤ÑT/áþ2ƒ÷k/†‡¸§rŒõƒˆ·–f ±X í8x¾KMð»y¾{Íi6ÓùÀ7At'–ÄJÕ‡´òÑÞ†ˆŽvÄÊUMŒè0qoù$µ Ñè!×|™n¼ ‘nì“îg!Qjʱ¡‰_ŒðkF®‹L˜*ûçO roG-y€Ú¸W…Ù†X³+ ÷ç¨}/ÊAÄ‚_N¸kdvÛ‘Â%ÈdYåH nJåB€Ö‚cH¬w+ÜOs¬p´£‰D£/=7©P¾è rùv}É“üYט’~ï¾͸†¶VÍ_×θö™¾HNBº“9™Ãx™CàöŠˆÏºÅ˜‰C‘`ªÅü<ƒé¢Ñø¹ã¯÷Ï;OŸg ,õ€—ï½½çÎ=Á×EøÄ÷â¤]ˆ Pö3ۖɸd2½ôöörøða6mÞL,câĉLž<‰)“'3~ÂÚZ[¥‡EëþÑkN49Q>”ô „¿F^rµb"t./“F#!Àà}\/£üྠbý®×ÑNFþ„9ÝTÙHe—ˆZ4kíOß…÷JaLŸ§öD{‘†C%&!Úc$Äs¿”ʳÓv÷ס. qû+¹|YF9V¸×‹GÜ("Ú!‰àùÂ=4l²âàST1Wš°Ð/-ܳŸZsÅÊfæÍhÄM$Å’ž>„I€ÌqŒ×+á³29,Ö–6ÈÊHv¾…=;骂à9®‚2*óˆe?4pÕ„ÜlÂû² å`€dÂð¦×æ‘“ùå:Es…Gp5¢:lA/ÜžL$èììd×ÎÄ7v,Ó¦OcÚÔ©L:•ˆ#rÆór½–Z°qùµw#éBÂ=®¢ü3ób`1ƒ ÓØŽøÉ—ã"öêÅ‹Ô.AæØfÜäp»ÁëCŒ?þ`Ò¡Âx!á^ä0*ö±˜˜G|ܳá!}z…?à_ÌCÚshˆ¥ùë7â•ÃXû2´5T¡˜h/e!/p˜/ìÕÀqȤÓìß¿Ÿýû÷óBôñ‹;–î®.´Ö§ÓàÔ¡ä8ªð_º¸æ !ÙÞƒóRŒnA&(R9÷#ˆå¿¸H8Ì¡îþ ¹ÖS¥ÙH¸¼iþöfrâÝ -S«È·ÜÀÊZ²q«5idD%jÙ#f?Ùð`%i«°ÿTa†¿Tâ·C]‘‘߮ҽlÅXá^7 Ž#"ÚÓºLt˜Ð!a_2}‘¯ýüµvȸivï=ĘØAV/îÆé:B*‘D~„g1BÑŽXÇî*h#„?)¾-õÜ">ìRàÂí.}Iͤ¶>wCïùV G{ 1jrn7Ù(;Åcµ›2פðØB‘¯ù \Ïåð‘Ã9z×ue†\oh¯õ(Æ V§û‰Ncð1Í«eÒ^N¸+Äj>‰G—¸šòþÕ.ð+êg¥L"ƒ‰Ob1_ŒŒ—X ž‚ˆôFê'¸kEŠ¡ ;éRÝßÂPùë@å„yc…ý§ ã‘Þr$€gëP——¡iðYFV¸×tªãDˆF£"àBñÐó––p‡ÉV ¶I—õ§ö<úúúØ»o7[¶î$Õû*ÿô'šÉ­è ,Éu~~šœ«L¾› ¡Jÿmý,òÙï¿m㇊ ¬ö8(åÐÝå¼ùG¸óÚ8þ³"Aç„)bm/¡©‹¥ËÛV$¯ Á¥)Ðu½ò!n,¥8Œ¸ÄÜDVYËÉ…š(?F¬îåÜ)– ƒÇþcùŽõ)'¦62ôQ+¸œ\ÈÉZCë^‹Œ/x •d4 É=ø‡&ïá"÷rN‰16IÔp1i–£“Ú0.‡¡¾Ï<Ë)ˆîuÆ@Öâ®´&/hc%ážu“Qy"W„nnR!×u9zô(;vì`÷îÝìÙ÷*==)þþcí\x–"8EHšÐgÈÂLTšg‘/vlÈBo‚è6ÊŸ‡‹Q©d”[Î;ÈK{&óíGZÃó|òŠÌ³¾÷æý¶»¶á¼CÁ H¸½rS»A¬î¿ úÙI—"Ó”ã—Ô/L!ÈïUÏòÂDËú›×£)ÃTËÀ ‚§lšáF!=g• }o_!§ÀËÛ2œXá^GŒñ0ÆC;ÑX'kq(1ð4›]å¥Fi‘þN$B:bÇÎlÛ¶={öÐu¢ c ½ ޾™w^¯ðÒæÔôÐ0ŸøyC¾'äÚ-Û‚°™&ˆó—´çÐ÷øä5GXßÙÁ3» ÍñPxI“›Õ4ãÆ˜²OÉÜïv•ñ×C ,0ÙüÀȽpÊ¿»jƯBü‘'!>®ˆrpÁ2Hä.àbí݇¸Ä¼Ê©1Ez/5æõ”~v*dë4ª›dÆA„~¹ˆ;ˆ›L­\+ªiPx Ï5_ |i†òO›?Ê!b´ˆJ Z‰½ÔoN‹°Â½þˆhM4ÅU:$ÆC¾í û¨(ñ:ç;â8­4½}}ìØ¶Í›·°{÷n‰ÆGÓݯYå3ïj ª É4#çÑZÔ&Ï]†œŸ|v£JLå€JùŸ.½ ×|œÏ\ßÀßmåX/Ä"AY*ßÕÅø£‚ –÷=”¾X/F~”þÐy^òà(âÖ²i†M±°5+ˆ5g4¤ÓÏY+¼^tÛÒ¥K×nÚ´iƒçyËËd½páÂ…«7oÞüDèЧ o»ë®»&}ò“Ÿ\ÓÕU:Túœ9s6ýêW¿š´lÙ²Õä®›*X7oÃûÃ×YΧ>õ©e_ùÊW¢©Té÷X,ûþ÷¿ÃM7ÝöÕ¯æÉaЬ—Úf\×ÅquðàAõ¾÷½ï‚{î¹ç&×uOÚ®dÂãÿMJ'ž1:ô›÷Ckí\~ùåSxàYHCÒ -nè3X/v^L:5²ÿ~e*¸Ã544¨¾¾>¥²“SÔ„ÓëÉ2t8TaèC3VÁb)‰îuÆ“…3)X*Ìcž•]d`$A;½½½lÙ¾• 6²{÷n2™L6¯ˆVô%=ƶhþêýÌœÉFŽh/Fø•äÛi 7åN0làæôôŹxÁ!þäÊñ«8®§ˆè.Òë1Ð}ò½Ô—Ó†°{L „*⋬°ø,©¥>ƒu‡\8ÀÂÏðRl[Ñå©§žŠ¾á oè|ðÁË ÷ȸqãÞìØ±±íííAC¤Ø¯=º0•J•Œ¹­”ò®»îº½Ë–-[@mD™3f̘±*~ªx¹ª©©©±&™tGïÚµ+~ûí·_ó裮r]w@¾ÏÑh4ÙÐÐÐÛØØØÝÖÖÖÝÒÒÒÝÚÚÚÓÒÒÒ×ÐЊÅb™††† Àý÷ß¿rÿþý³Jå‹Å¢W_}õJľÒ9Šøl£óŸÿùŸÇ¿ÿýïo;zôhɃÛÚÚZ¾ð…/,ÆÌh—]2¡Ï ¹F­)Xï÷ùÅ/~1ò™Ï|Fgߥ±b³<šÊñÛAqÛð`–ºb…{1þ?ÇqˆÄb(­ ÞéY?u¥roy¥òÜ-´Ö8Ž&™L²mûv6lØÈ¶íÛH&“8ÚɦSÒð<ÃßÒÄê©^3²E{@¾gJŽ`W#穌";@5χÞÁ3ŠÞ>‡[Ï;Äó“øéºÍŽÉ^ó~e!¿ªz_9yåöGa¬¢hhhˆõõõµ“ßa1¬ >£þêH‘õbß Å¹ö?s-¶|ªÝ–·½¹¹Ù¹ñÆ·=ú裉t:]òžqãÆ%O=õÔØ+¯¼òp©4étÚ<òÈ# ’ÉdÉpmmmG®¾úê]ÈyÔB ¤¿Ç0°ôEïÚµ+zÛm·]÷Øc]ZíA Ý'NÜ?}úô=Ë–-ë\ºté¡E‹;묳NtttdÈÍÞ ïì³Ïž[N¸¤¯DpϦ×ÍÍÍãÇ)ûnmiii>ãŒ3ÎfSüú÷mÐ Ä|XÜg ¾{ï~÷»Í÷¾÷½/¼ðBÙÊŸy晓֯_?³ ÏÂÆHvŸRªÚ{o´ˆXEuúhÔ<È-#+Üëˆ1bqDbÑÕßÍ¢¸Õ]FH&Sl|é%ž~úöìÙK2™@k-dBægÏ@WŸÇ‡ojå7Eq3§ŽL¬*˜Ê`ayfBÂXhr“4‰qÌ%Jc$Íç®;ÄÁ®É<¸IÑêOi10kz¾?(¿Ô€Vå߯;u~“BŒ1‰üˆèðzð=Ä.¸à‚ùk×®-ë°xñâ™ÀÈ·œ‡{x)Õ4+çvQì{à7_k¼[o½µóË_þòÎ;v” yâĉñßùÎwμòÊ+.‘D½ð ­Ï>ûìÒr…-\¸pëš5k3zQ€êîîV7ÝtÓëÖ­»¨šZ[[¬Zµê‰n¸aãe—]öê¼yóúûÐ,…¿»J&“*NU­¼ûÎTò‘ñyžÉåþйqèx<®§L™2±’p¿è¢‹–#3 ‡{ Ý‚²ßÉNŸBzaŠ.7ÜpÃÔ_ÿú×Ý„8õ¯¡:ßõ(£Ã$fAXá^Oü׊ÖÑHÔ\šß3¬u¾hŒD"¸—Ý»wóä“kÙ¼e }½}(­ÅÊ䢧ÏcÙ¼¹%BÔä0RJÎÉÑ sˆ7óÍE\ÿšhÝ*2®2Ü{ÑCD¼èD:ÂÄÖn>yõ Öïiçx|ös—óù½ç…è,Q±‚ã_÷ eJzO Œw’Â%xᇗ8ò²‰Ù^ŠùIæ™={ö´§žzªì‹:7 ñˉî@ÄœêxãÇ×+W®|±³³s¾ëº%Ÿ¡k×®=óàÁƒutt÷Ýwß´ƒN+u|$I]qÅ/2:…ú«¿ú«åO?ýôÊJ #‘Húì³Ï~êÎ;ï|ø¦›nÚG¾Ïþ©>ûf-(×@ ãc´çU5³[`Qvè•û^ØH »ê8W\qÅÌûî»/šN—Ö¼Ó¦Më¸ûî»W­X±â(Ò& Éжà™Ð¯\ÇqT§:Ø#ê¢Å´02B[ZFV¸ŽÖDcQ_C ô}Ý#‘Z+öîÝǺuëxö¹çéêêB)…ã”6¥2í­š¿|SÇ;$û†Ø°d¯B"Ýå(ðg;íIŽÍй§—›LÓ0sŒm„¦¸îyñÏóç&ʺþP°2Â3ÓêÐc< ZÓÝ×ÀYÓŽñÑ×5ñ·÷Å1€õ„d‹Î³Â–Q _ÎZo Iç½u|^Ì$ü=<¥{Œþ<^°­œEh°‚0ô«W‘XT}á`¾‘Œwûí·o¸ÿþû/9räȤR‰^yå•?úÑf~ìc{™"ç~ÿý÷ŸYn°ä„ öèCÚÄ(´¶ÿö·¿÷ƒüà ÏóÊZÁzn¹å–_ßu×]ÇãA¨Qw=†ƒB!æò“A"\Ub"òŒ=g¶ŒR¬p¯ b ž÷hT&úS*'Û ¾¨Féíëcà xøá‡yå•W€ÜàÕbÖM¥Àõ ©´áη5qÅù’}µ²ëæŠV€ÖàD5ÚQàÈ“„cÝ.{gؾ³Ûúxig’—¶w³igо>hk„¥Ó`ñ4‡ESaé4—y“aB+´6@Ä‘r\Ò!1œLUçã½ëÓ]…t‚qð ô¥o?ïU^Ø=•_>ïÐÚ@ÖW^®;ä½[ µVÐ0(Óe/æ Æ¦L™2öÀ¾= é/¶Ã¢»Pœ~ÛÙÚ|–ÚVŒÑ"¤ë¹òÊ+.Y²dóc=VR¸§Óé¦x`‘/Üèõë×7mÚ´iQ¹BÎ?ÿüçg̘‘bôýNæë_ÿú²ýû÷Ï,—(‰¤n½õÖ_ûÛß~,8®u³ C•nBáô”ÿM#ÈŒ¹P¤`Á‚s* ÷3fLúüç?2'AÒXöƒAšÜxLø»RêHç1y>wW‘¶VX ÿiŽîuÅ€ñp"¢‘¨¸]hñ:PÈŒ§(غe+¿{ðA¶lÝJ2‘$qrÇ—Àó «×ã=×·ð‘[cdR^6ßZTZ)¢1 æ›×ÓGNxtîKòòîÏoîá™=ìÚ—áб ‡Žy44gÆôÙ,9c!7¾ã¦M›Æ¶mÛøÃžá¡í;øáÓ¯é;Âäv‡q-Š…“aùlÍkfxÌè2­Ý£­Qô¶ñ ™´ëP:§¯œ@ò›äÖq㟻î{NtðÔ%e^‡°ÛKx,jIËz."P0y–ça* æÏ¬ U%ü"+õ9ÜIF;Ð7ÝtÓ3ëÖ­;7•J5MdŒZ·nݲ§žzê±óÎ;¯‹œ¥Xýë_?ãØ±cãKÐÒÒrüï|gyG呉Z¿~}óo~ó›K*¤3çž{îSV´Ÿ¶T2>¨j\‚ZZZšZ[[çѶѼÁ·ôìkÞûÞ÷NûÖ·¾åùc~J1Ò¤IS8PrzÑT¦Ò2Š±Â½^$ÀDLvpj4ÍF# f<=~ì8>ø O<ù$ÇŽóÝbtQ {!]½KçÄøÄí 48Šd‚ú“uw‘:¥AÐ6о—]ûRlîLñâ¶^ž{©‡-;ì>àâgÌØ©,9ã Ö,=ƒåË^Â…ó™=kMMM466úâÕ¥··®®.^~y[¶nå…õxqÃFžÜ¼…‡¶Æd’4:)fUÌŸª9k¶fé4܉Ú24Ǥ¾®Q£ðüÅå«_'8%Ÿ!І§ô¥£LjKñÙ«»ù㟵²÷¸¡)\šü¨ŸzaH‹{ñþã[Üý‰°‚xÑ#‘ª,lALmF—øro¹å–_ýêWwlß¾ýŒR‰Ž;6áG?úѼóÎ;ïyrçï­[·n^&“‰–:nÑ¢E›.»ì²W©ý½QµUÔ³…ÕöwSßùÎwœ8q¢d£`̘1‡?üá?ê­É5¨&^ºëº'{ÎFk]Íß¡rjy}‡óo2p‰+‹çy'{o@U;Øu]—ÒaMÃ.ˆa"Ë—/×Ñh4YªaîÓzÝu×½ç›ßüæ#È`ß$bÍO >òÉж td8´§€ûÈX¡á˜˜Ìr a…{ þRµ–™S '*‚rËæ-Üsï}lܸÏóÐþ ÕJO&$S†±mŸ{O 3&i’=¦¬h÷ ¾âîâÈÀQE|_\—®>xõhŠ=¯$Øðr‚ /w³öù.¶ìréM(š[ÇÐÑ1™Ž‰“yÓê3Y~ÖkXqöÙÌš5‹±cÇÇK–¯µCKK ---L™2…‹.º€ÞÞ^Ž9¶íÛyöÙçxþ… lÚ´‰ßï}•_®?Dª·‹ö¸Ë’i^;ßaÑÅâiÓÆ)Æ5§iŠËùày¸žGÊázžQ~ïFØp’oDQÊГŠpÞìc|âŠ(úó\£È;¤P˜—šx©Øvå[é• ¿­Gº ­Jú†±‘|žE™4iRzõêÕÏ–î©Tªá÷¿ÿýbàü?ÿçž{®içÎ%ÝDÇÉ\zé¥/¶·¥ã$ IDAT·1¼kÉ@ò’ûóÉ'Ÿœïy^Ùîþyóæm½í¶ÛöRß&UA;Ü £Á¸«Ô¬ìêÆÅæ•=ØòÛ˜+,¯T>fÖ¬YÇz*wÖ¯_¿ xÕ…y†Ï3pÑ D|qßÉûN~O@6òoΕc9M±Â½žø¾Žvˆøî2ÝÝÝ<þÄÜÿß÷³oÿ>”ªtÕ3Ï2®áConáÚ‹#¤ËŒƒ7Fôc4"¾éDxŠDÒãða—½ûzÙ¹'Ŧí=¼´£·ö²}w†¤i¢£c3¦-áš7Ìeñ¢…,\¸€3Ï\ʼysin®MÏ]SSMMMLŸ>Õ«Vpôè¶mÝÎK›6±eÛ66oÞJgç.¾ÿÜ~Žüî­‘.N‰°xJ„ÅÓOÕLç1uœaL£¢1.ÁPÒCÚ5¸žƒA¡‚–K1ÈÄXsæq~·)Æ=ëZâPhqÏ}WßÛ?Mà¥òäú¨Ò±§%7ß|óæ_þò—Ê Rݵk×Ìgžy¦yÅŠÝ€zâ‰'&”s“éèèxeÍš5;‡¢¾mH Geß¾}Ë¥ÑZ»Ë—/ßA E¥ëº¤R)û¾óÎ{ ÕþæÕŒò,tC,u|¶WìÜsÏ=ÖÖÖväĉÊ•ñòË/ÏݰaC|éÒ¥=ôoŒ„¿GÍÕR°? ·Y87¤Œ1ÉéÓ§_¶gÏžj^¸öå2б²º‘À˜žç‹Einjfë¶-üè‡?aý‹/’N§qœ¹çLuœèñxÇu-ü¯·7àºà¹&;€S)ÐŽ%ÅÑà€IuÙ²'Ás›ºÙ¸µ‡M;’ìÚçrôD†=M­,Y²‚K¯YÎ/fñâ…Ìœ9“ñãÇ1fLæ©)cÇŽãÜóÆqîyçJ¥8qâdçÎ]lÞ²M›7óÜó/ò«û·â%ÑÚmšYã=Lv8s¦fét˜>Î0¦Iu<2ž"ãŠX7PJ“r¡!êñ—×u±÷x;ÏîV´Ä+G)½.ß1yñƒŒ1wH,nõ¤Úºôó,…yýë_påÊ•OÝ{ï½×—JtøðáÉ÷Þ{ïô+Vl܇zhN2™l*•þ‚ .xöŠ+®84$5€EvÜ)ôÚµkÛ»ººÚÊ&ÒÚ]ºtéj‰èرc‘d2Yq&ÌXÁ«ã>d÷ª ]ÙU¹¯Pþ™0û³r¥ŠŸk±–KÞþÉ“'«¹sçîØ³gÏ‚rÑŸŽ=:åK_úÒYßþö·å[¬±P¸^¸ÍŸl„FɳÜïÙ³' ¼‘ ƒSµÖjñâÅã7lØ0 ÿüH<Õ^3Ë©îuEþNµ£YûÔZ~òãŸðòŽ~Å`ÕêP ºº ç.iàsïn"7¥ˆ7*p4x‰”áx·Ç«ÓlÞÕǺ]<ób_Ns¢×E;ÍŒ0…¥+–pÁÊóX¾|óçÍ££c---Ùè7§ ±XŒ &0a–,YÂ5׈˜ïêêbÿWÙºu/¼°žuO?ÃKÛwðÔÓG0k{Q&ÍøÆ g΀e³¢,›¡™=Q1±MÓ7D# Qd ¤Ý(“Û]>{Mwü¨‰ã}Ð…<«z¿©ý·çV¬«`0rÞ3{$ Zãy^U"…‘}žåpÖ¬Y³ñÁ|]"‘(:ªçy‘‡~xÁ§>õ©{öì‰oÚ´i%,~ñx¼çª«®Ú‚¼À‡"FyÕ¿A ü½û•}àÀ†T*UÚΧ­­­R¨À ¶mÛÖT©Á⤄{ÝEJåY‘¡òq¯qT™RÇî50Ÿ…ëŶy—\rÉKO<ñĪr³$»®{ôÑGW;vì‰ööö BQοÜþ°ÿ{^Ãõÿñ—¾úê« JÕ#@k­gÏž=˜ƒÊ—>9†ió#dN¸KzÜfçž$/mëá¹Íݼ¸µ—Í»\Ò¦ñãÆÑÞ>Ÿ×œ;%‹qæ™g²dÉb-ZD{û˜ŸiýˆÅbŒ?žñãdzôŒ%¼ñ1~;f×®]³K¥ëèèØó–·¼e7¹n­©‡¸*™_OOãºnÅL‰D`ɨEÙæ¡‡šrüøñ².ä‹¥A ÷jŽ5>'YÖ Ë‚F àÞ¢s/ZT¨ŒB¥_ªì`»û¶·½mû¿þë¿8tèЬr…ìÞ½{Ñ]wÝ5÷ÓŸþô6òÿvKYÚ ëWl §ñî¾ûî+Òét+Uà‹î`6ê ¯`ò½Àš¾.91ŸÒƘ~‘vBÇYa?ŒXá^G\Ï •bݺ§ÑZûV~ï{žAo\å°kï þó›w$Ù¾»—{ú8|ššÛyÍk–³ìÂ…Üô®E,Y¼ˆyóæ2aÂÆWvðèh¡££ƒŽŽ.ºP¿öõöräèQ<Èö—_fëÖí2vË6~z÷V¼¾ŒoSÌcÁ´(ó'iMëå‚ÙšÿÙæ`tÖŠLógT Ï• )]äòzfH^žõ¤êéÝÙçY3sæÌÔE]ôÌž={JΤºoß¾™÷ÜsÏ”7N8qâDG±4Žã¸«V­úÃøñã]×uã8C)Ü+æ=VYÇ]­uYS©çyzÇŽ%ádËVû÷ïw~ðƒ¬r]·ÚîÓî!Q^–¡´]eÑÙäµ,»Ú¼ î«“)¿ò´©â’Žä’·»Ø!á/‹/îYµjÕã?ÿùÏË ÷d2Ù|×]w½aõêÕw­\¹òÅ-êÅÜhÂi¼ßÕí·ß~aggçåê=ct*•rBÇ÷‹dS°ûƒ;ÁÌÚáýnèÓ\ÿºšÐ¾lyVÔ-V¸×#"å¬ìƒ7ê$z»øéáß~áqäDÐ,\¼”w}àZÎ;÷\,˜ÇĉŒ;–ÆÆ²ƒâO›š˜ÖÔÄ´iÓ8묳èîîæØ±c:|„-[¶±ö©u<úûÇøÑ[ˆ9†ñ-Ç9†ë6¡bMî/¯{h[¿é^Ãn4¹ ˜B¿ýH´é«ÉçY ï¶ÛnÛpï½÷:~üøäb 2™LÃÃ?<§³³³ä Öæææ#7ß|óÀ"ѳ”Õ\¸O˜0¡/–eÒó¿ª3Í?Rù>_ )vLèØR|Z71G‚ucLÄ£ý¥–»Ÿ6X‹{=1¦&s™*ZÛÆÐÜâây®ëâf\v¼¼ƒíÛ¶áyžçÑØØÄøñã˜0aÂÿß޹ǹQ\ùþT·¤Ñ¼5¶gŒß0b &ĹöšÇ5á¹`‚!<Øò!Þûá³ Öönn6$$7χÜÝ% ×$!„…½fI ÄNl60ÄÆ&ÄãÏÃöxÞ3šI­®û‡ÔšR©^Ý#ÍŸ¯?²º«Nªni¤_®†ùóçÃâÅ‹aÑ¢E°páB˜5kÔÕÕA]]ÔÔ˜^³5µp]úúú ¯¯Ï[†ššš ¹¹ššš ½½º»»¡··R)B!ˆemÙPQQ!ÛË e[`YÌŸŠ÷lJŒø.µ’¼öÌwQ™`è 7ÜðÎûï¿ÿ_d) d-ËJ­^½z÷dº¼KÒ9sæ¤fÍšu´µµUz=@GGǼ‡~ø³7n|Se§À~ñÅëxà›zzzf ŽR;›b0¥ñ1)+z×EhO˜í1Ã\<Ï?Ëú/؇ÃäÞ{ïýÏ={öœÓÝÝ=OÓ_ä•W^¹þÊ+¯´^~ùå_Ù™Ÿ×½üpS± ?üáç?úè£wvtt|F}”‚QOŒDÇ*+çÇÈ—‹ì„Câž½ 9ñÞ¯„J)%‘׃Â}¼ ð–,\Á$êµ, X„æüRJfo@”J¥ ··:;»àƒöB:‰Î‡ÃaˆÅbÐÐÐõõõÐÐÐ .„ÓN; æÍ›sæÌ††¨©©H„¿¡Üäcddúúú ££Ž9ÍÍÍðñÇCkk+tvvBWWtuuÁÀÀ¤ˆeA$P( ¡P*+«À¶C`Û6+9'„€g¿M€ð¯‡ziHÈFä@f}~&~H]ÊF/¦*&y¦l”çËÝwß}ðŸþéŸö666žë·íܹsï¸ãŽ?CéÏ“÷í¨4¢”Bö–òEÏ%—\²{×®]A·’O¥RÑM›6]þÌ3Ï||ûí··ù˜Gyä´'žxâÆîîî٦㢔Bæ&›c;ælš€Î†2¹×Åúû'žo­aFûµ%Y¡¬|sQJ½Õ¨LóÎ¥~´ʈA6Z,t¥(£@®»îºc[¶lÙô/ÿò/N§•_Š©Tªâ׿þõW–.]:ïÞ{ïÝüÕ¯~µ‰ñÅŠ[>§ìÛ·/úÀüÅ–-[næïyàN›6m¥4ÔÓÓs–ð`(µ‰D„9f^¸»’:Ó_úDÇ¢³=?n토pGLb¬Þ*¥£9ÔüÇ …ŒP'„€KÓy«—X–Ô"`“PæT’‰œ€Ì]DÝ4¤Ó. $ ñàAøÓG7íB(‚šš¨¬¬„êêj¨¯¯‡E‹Á‚ `Þ¼y0wî\X°`Ìž=*++¥‘äRB)…¾¾>hoo‡––hkkƒÖÖVhii¦¦&èéé¡¡!„ÁÁ8¸Ô…p8 ‘p"‘T×Ô‚m‡ d[`Ùöh‹·ð=Íœ[/ý4ïÕ’oNܾ^b>û#"eþ!ŸjkkÓ^xáÏÃÄx¬X±b×i§6 ¥ŸÜLô›Î½óÎ;÷?õÔS-º•:úûûëׯ_sww÷ ÷ÝwßÈ?§lä.Çï~÷»ºÇ{lù¶mÛ.òukx×uíáááÉó“Ç'Áuüë©}ŸfÓ}Lú2Y)HmgÚ‘üÇܵuëÖ 8p¾I·ûöí»ðßøÆ7n|ó‹_üâ»wß}÷ÇÓ¦Mó–x-Ëo~ó›ØO~ò“O¿ùæ›+=ºÔqœ‚‹ÓÂápÿ—¾ô¥ŸoÙ²å¿Ê„;ÇqØ éL#î~~ÐùäQFâ½h»¢=€Â}œ8Ö×çN¯ªÙû—ìì>[&+gE¾KÝÜ <Ÿm¡—jFs¬fs-ÁM§ÁI§¡`ººº ñàAx÷ÝwÁ²,°m;oýôyóæÁÂ… aÉ’%°páB˜;w.TUUAEE”••e¿t"NÃÈÈ C?´¶¶À¡CM°ÿ~hjj‚#GŽ@ww7ôôô€ã8™ÕY²©A¡p"ᄘQ_™Ë)÷VðaÏSî“&{žˆ r.\9F¶–»à¦LB;`ÎùÔ_ßÜϸ§òq³fÍš?¿üòˇUi1³gÏž¥ÝÝݳEÑüÊÊÊ®d2Y)[“›Rj …`lpãë>J¸¤Û¢ömºbQö— ^ôÈ¿RéÇä"`ï»AâK&8Eû4‹Ñ¿ÿû¿~ݺu3:;;êúœùÖ[o]»k×®Õ?þxãܹs›çÎÛ^WW×W^^îô÷÷GŽ92£¹¹yþÑ£GOÇã³Òé´tÉ·3Ï<ó•»ï¾ûÃÍ›7AfC)µ²«[ù‰Ž ]qu&Â\61ЂÑvsP¸—J© £Ë*…|ðÁ¹Oýä_-þC]˜#À¤\'óY äÄ9g›—.¤œT*ánƒ÷Þ{?Å'™´›†úz˜¿`œzê©0þ|X°`Ìš5+—G_UUÑhl{4}Ôqœœ8ïë냶¶6h?rZ†Ã‡Ccc#´µµAOO¤Ói „€mÛYnA(‚h4 –Ûʤ¸°~"ÄÿªjIÎ{î9÷·»g#8ï¢;ª²ûü„æ°L".™]¹re÷§>õ©ü÷yóæí¿êª«ŽÂøœ꺮ÑÅ©¥|ÝÖ­[÷‡;v|¦¹¹Y»ÚK<mß¾ýŠ7ß|ó¢Y³f5MŸ>ýx8Néïï¯êêêšÙÛÛ;Ëqi Cmmí‘[o½õÅçž{îjY¤ŸRjŒŒ„alÇk¼$è…SàþuF¼Ž»*²ëa´Ô£é ¸¾”Ñ_…{ã7Ù¿ÿ³ßûÞ÷¾Çu÷ð ©Tªº½½ýÜöööswîÜÉöaež9sæ;ßÿþ÷_¨©©I…B¡”¦?oª:Ç¢sbU× Ó îc$1*€JæQ}Ž@¢]tÑì}úË¡~RMtâ\VžMxô ´öl´?“ãmMlˆ„#å£ÖÍæÑ»4sQlwo/ttvÂÛo¿ Žã€mÛP[[ Ó¦MƒX,Ó§O‡¹sç¢E‹ ¶¶6³ŽúÁƒpôèÑÜÅ¡===088™*…ÂagóÏ-+“Úb[£é-yË- 4?½ˆ9N‘ –‰káPEÑxOÑ6?‰¢i`jç¸Ã… Ç ¢¢‚\}õÕïïØ±ãÂd2Y¥³/++¼ì²ËÞÎ^”:^ï£~JymÂç>÷¹þûî»ïßî¿ÿþ¹###Fws§¼¥¥åŒ–––3üôU]]Ýñ·û·Ï\sÍ5-/½ôÒj•m___Æø:¸!wfU™¢ç¸Þ͸¯-1ÕM1ºk3!„fSed}™DÛó¶~øá?E£Ñ=öØc·uvvú]í%ox¦†Ó¦MÛóØcýàâ‹/îknn…B¡•ßD"á½Yq­Êmå·ƒ ÌDìƒÄui wC²Ý€*¨Œ0¯€dÄy2ö0d–=Êû©R⺂4S!n’‚À}ÈÄ©¢Nå›BæR¶eØ(ó&x@]œtÜtŽë€ÖÖVpB¡LÎ}(‚T*Éd\×…H¤ "‘p6½¥l+U÷î4J ÄÊ<{¹á¼˜&^‰"ú-Óâc‹XÃۜש‰Æˆ{!îÚµk›~øÃ6·´´h£É ‡n»í¶`tå‰Rc-QTÖ#½nݺßzë­_|ñÅS©TIn6‹ÅÚÖ­[÷ìúõë?:~ü¸Fã*ûx<^žL&!‰”&)0ú:=êíÓvÜ#îܧ‚Feµ©4¿~D:¿Ÿ·}ÿý÷7ÖÔÔüóC=ôß:;;ù‡oêëëÿðÀsíµ×vAV„Û¶íê„{öÂV~âÁ¯gÏn«Îì!²IRDNXážéÍC¯€Œ@¯€ŒH¯‚Q‘žk–}&Ì>»L™HÀ Ôé'•(ªËv˜—b!0ã׆—ùã}8’Øf_Õž?xv¥;‚P8”‹dÓ‚Qúc/ %…J:Ñ&‚6y~$õ…íEb›(ûæ·ù2‘].7žïuœòÂÝq"ºÅu]ö¦6'îœ9s:mÛN9Ž#îµµµGï½÷Þw¡p¢_Jh*•"ª ¥ÔN¥R¥ÝèOúÓmëׯïÚ¸qã•---gÕa,k¿à‚ ~ÿä“O¾vÊ)§$™UWW+oþÔÝÝ]700`•——›.?ÉCÇÕëN§Ku~©ëºÚÕ’É$+B‹Ù·÷÷.%N[àÿ³ÎÄqmÀÀq›É\2î|¿'x׬YstÕªUÿçë_ÿúÞ-[¶\ÞÝݽh¬«£Bh,Ûá…þÛÆ[UUÅ®ÕÕÕiBˆ*Ç’Éd%äŸã BÝ$˜`"âu“#Ä''„pÏþ!U0/z…Ñ<ô0òE9d-ÐÞ^WœÍ¨æu]×Í^ðI)5^yE–JC)'6}¤Ö˜¶ãŧ.o;¯œiËm2v’(µ"åDT&·Þ§dÜŠ4™<[ɘ =[gY@˜÷@ r\ÇX,6P]]}Dö~v'TSSÓ 'HŽ{²gÏžStë=¯\¹ò·+W®ìƒq~ýkjj†c±X["‘¨ÌæÿæA)%‘Hd¨ªª*ãôº}ë[ßÚ{Ë-·ø‡ø‡ó·oß¾¢½½}‰ÉÝN=lÛNÎ;÷à .¸`×í·ß¾ûòË/ïšN§Á¶íÜø/^ܺoß¾vÑšÑétÚ¶m;ÕÑÑjhhPF4UTTT¤êêê:(¥¶mÛ€t:mÇb±ã•••Þù-jŽ{]]]oUUÕ1˲\>m‡RJ\×µb±XJã^QQ‘¨­­=â8N™è½•N§íººº®P(”Оˆ®­­¬®®î „8¢ÏÇqB±X¬;‰8 ?ϲþeÑãQ___ŸúÅ/~±í7ÞøÃÓO?}æo¼±¼µµõÓÉdÒ× #‘HßÌ™3ÿxþùç¿q×]w½ùå—÷‚$ÿ|úôéÇ¢ÑhŒNús㢔†ÊËË»{zzH]]/ÔUk·‹î¨ªŠ¸‹&8ªÉXA9æ·cL3ÃÉ3Óe/õrÒ½ucÕ{T*‡‚ááaXvþ²_oyýõŸÂî(ß·o_u(~§R)kþüùCŸùÌg”) Ÿ ¬ßþö·±ë®»îôööΗUWWùå/ù¿.»ì².ß 9räHx÷îÝ5©TÊ}qRJI8v?ÿùÏ÷ÔÔÔ<ÁرcGõ¦M›Nþýïÿ©Ã‡Ÿ2888-•JU$“É0¥Ô²,+‡G"‘ÈP]]]ǼyóšW¬XñÑu×]×|î¹çä-+˜GSSStïÞ½Õ"ážJ¥¬ÚÚÚÔòåË{ËËËÿMÆãqûwÞ©íïï‰þ.Ç!±X,uþùç÷WVVýüîÝ»·òÀU¡PˆòÇH)%étÎ9çœþ“O>Yuc :;;Ão¿ývLt~2ÇÞÐÐX¶lYÀërmãÖ[nýN"‘¨…ByK#ð_ÞS0±njk÷!šÙaëÖ>ùÑ#H½lb‘«×ü Šô›œ'B$ 8t茌ŒÐÏ/ÿÜÿÛ¼yó/a w0ÿ 9Q> ­¯|å+ñÜsÏýµ*å’K.yþµ×^ûw6<Θ¼n:¶d2 ]]]ßH¾ IDATá={öT{k¢‡™g‘0¦Œêlkœ­JÈS® ¤¬¬,Gâ”Æg°Þó7÷áÕ¶y¹ Ê«°)°clMû`õ¸Ð¿'€=#_]¤:o<º±hë¡ ^—ÛÎGÛ…mEÑýÑÆÎ‚”ã@:íqgΜ٠S_¸# ßúÖ·.9vìØ•Íi§öî]wÝÕùŸ1‚ŒSnR§ ²è¼JЋ¢Ñ²2“‡*_ݤÜįllü1úúCÑ>6&T¸gsÒ dÄ8+Ô+`4=£ùèÞ¬0(çº?T^p›´ñìLÚäê¦M›–,F{»]wu-Zçgz”/ÓH$v²r~IIÑÁŠoÈkÃogl<ÿâHw¾,æÆî-)éC´ëÆ*·Ø'ÊãÖMØ¢4oƒR†tæ&UéSN9¥|è!“òíoû´wÞyç2PLä+**ޝ]»öwY›©œ"… ãû[ŒÏI™@—í›Ö‰¢î®¢Þh—Ù˜´UM4t~Eöº2Ós‡ŒqîÌ Œ,ÈêåYŽÑ†üµÑ½¼tï ^¤›þ!SÆ—Nسõ²m‘Ôçê¦M›–¨‰Å:ZÛÚ2weš°B‚\ f „¶²Üqv_æË$ï\–#¿Ù=娏²s}«¨Lzî8±.›a¤_QÇ¿†Þ±º® CÃÃ@ RV6pÞyçoŸÈ®]»*žx≓ÉdµÊð¬³Îúýƒ>¸ðuGX!éW¼«Ú˜èDv¼°•‰wQ™nÛoA¨ÈF'øuÈÆ+ª×ù@ŠDÉ„;s3#o—(dº÷ðr@EQs—«ãß(&k)²b_4kW n••­Ì=餓óæÌ>üá‡û€R\š†Ê5Uå•Ëöu¹íÒ‹E}ø±aíø¨sª¸ùÇ-ðiy'|Y¡O3?ùÇ!³®?’wŒ…ýyå–E •r >4”ºP«m9ãŒ3ø¼GdêA€~ík_»öرcŸRVVVùÆ7¾ñäßûANtDß~Ŷʯ¬IªHµ¬¡.²S‰kQ=êLÐEÜUþdçC¤Ó„`šÌØ)špÏ^@‚ÑtvùÅä u ™//ïF l¸ÓÛv™}ÖŽpul¹·ÏGØùp*uEáUÑv‘8gíEXçw^ãÖ­[Gœt:šN¥!l3©ú4+ø²­GÓ[x¡H Ûä "u¶ ›Ò’ëƒ0íˆÄ·ÄF%ºórãÎGt\D0¯^7V"ð-*3éŒáÛ*ÒiDýBÀ"  @b8Ô¥pÊÉ'ÿ©¾¾>(ܧ:äú믿ä½÷Þ»RuA*!ĹôÒK_Z³fÍ1Àh;‚ˆËg¡Jœû›DÜE¾uÂ\¶íí‹–TT=D9î )gëÛ× u“s(š˜ %"°pg„º·ÚKd³Í<³B˜}¶œ½‹À¨ð&\™(ê-ÕÀõ§²+8,I¾½*B ëÇ=ÿüóTTTvôõõÍwÝ´ðFLºˆ°jÛÛ7i«ó!²QGáÙù‘¿~MóÛƒ´5õÃþ™ú˯ïï×u! %Ï>ûìýÙ¥ñCn sÇw,ÿÿø[t7[š3gÎ7lØð&àÅÈ"¢T~¢í²r•Xgëe‘l•pæŸM¢¶ª¼zѸDÇ¢«3ó<0Ú^$Œ„;s©—úâ vÙZéÞ›’@~ô;Ï-ŒŠw>ÒͶ÷lMÄ·*z.òÁNt‚Ýø÷ÈëëÒK/íœ^?£±·¯o¾ëRH§Ó`1ë¹—:O]—³.òß ê3Åšþt}™¦Æ¶)N*LýèÒsdǧšœ3°lF†‡¡¯¿\J¡®¶¶é¢‹.jÿ~Èä€ý«¿ú«Ï?ÿüóH$b*ãP(4tà 7ü{öæ@øz#È(üwt1|ÅÎO´]U&Ì¢6"1=ñ®³5™ˆ¨ÎƒÝD¨¯/’E(Ü¡N`4ÍÅìl]® ˆE0påÀ•c+zQYÁm‚îÍ!«WµS vS1ŸëçœOŸó‡æ¦¦Ï§tÔqGÂL »‚ ÍÌ£]æ§@~[’oS'†<›üôÆ–‡8Í…µåÛ@ _O˜ ß–°õÜ8%mXÙ•jDç‰ú󎋎ˆî˯¥ïÙ Î9°ÞÞ^ æÏ›÷ÁòåË»s§"¸×\sÍ^}õÕ›‰DʘBO=õÔmßùÎwvg‹ðË A )Öß…ÎN`ÊÊdúEöìm«Ä¾N4«=¿/Š‚û¯ãdmùñªöóÊewÔEü‘îÙÔ/EÅ»+©·ï=Lfª4ÛŽÍC'PxA)û&aë)³ÏþìC8{")Í6ûÌ׳ý°vü>‹J¸óuîßüÍ×wncÛÁÎãg¦R)ˆ8‡GsÝ…Ñ\FPò6º\nv¿ _m* aNª.Ò'ŠýEÉ•ã{áD@_¯Í‹LPôçÉ‚D" GŽ´“†²²HüÒKW¿UWWçæ:O5ȶmÛªî»ï¾5»wïþK×uùûIÐÐаçG?úÑO³·eÇ×AJK©„;¿¯‹¸ËžME8€úî¤Þç k«Š°ë¢qÊ|ÉŽ'BÙèzFÅ2«´Ø„åáE«è@ԗמßA4¾Îo^E—ùòe¿lÙ²OŸ}öößmÛ¶$í¦íd* áH8¿fŶ©ÂÔ¹/_¡€e› ®Üwc“[UoÒŸ.=I˜b”Ý´m Žwv@|(.u¡®®î£»îºË[?ð&? “ 6|úg?ûÙÚãÇ:èPFõ«_}fõêÕ½€¢AŠMÏO?Â]T.‹j‹¶ƒ wQ?‘uÙDmE>Šõ½T0.Œ¶/âîEU‘e•Ðæ÷E/¾Lè{Ûgk*¸ù¶º1ËŽ u² ƒÈ‡±Ï{î¹ç÷ï¾»ë²þþþSRÉ8"‘ükÛDé)~·µ¶T&¸¾õÂ2ö”â:ÿ´¦í˜õ'jkZoÜ•«à<3Û¶!‘HÀ±£Ç í¤! 'V®\ùúœ9sR€Bn²ã}&º?þøÉ?þñÿ²±±q•ã8U&mÛ¾âŠ+þõ‘Gù°´ÃD?BPe+½*UäY'ØEeºH9_&ÚµS uU½È^´-³aÑé9Ä'£ ‰«Å([/¤|½Hà«„½ŸVdË^Èj:&ÑD…-W ~Ù¤@<Ž<®¼òÊãçœsÎkoüç÷¤Ý4ŒŒŒd„;aD Hn[d£µ•øå#˼r>oœK—Q]´É ö¼|ðَܸ ÆÊÕ³eÞøu~D©1Fç”í#7v¶ždχÇÃàà ̘QÿÁƒnx 0·}Ò³sçÎÊM›6Ííµ×>ûÑG­ŠÇãsLÛBœÏ~ö³?þùç·–rŒò ¡˜¢ÎÔWÐÈ».Ú$êÎnÝçSeDã7©ÙóÇ(k#³CŠ {q*/fÙr‹)7ÞlŽ;0mçïƒ}fÇ%zx>dëÁS-_PxlüñˆöÁ Œ'góÈ#¿~ÓM7×ÞÞ~A"9CÃ!¨ª¬5ÌÁêå_@ÉÖƒ ¾0:,¬ÒFÓ^;¹Øgv@9ž‚6>Æ/ò#<§9A.™ ä¥ÈØÐÓÛÍMMvÓPVVÖwóÍ7=»dɩÀl“rï½÷^õÇ?þqM*•ªñÕgéÒ¥/ìØ±ã9."RH)þNüF’uå~Å»êY%êE"[VçÇFg+*×#¿Œ!B¥”òeþ…‘‰T^ ³6¬ –µ#PøR‹ïSÛ¿h¬²r"/²!Ë—/øò—¿üó'Ÿüñ§’©dõðÐD"(++JižÏ°ñ;†ú@§ |ê£ÿÙza=!ò%Å¢~lçDÖ_P!L¥àãƒaxx€,Y²ä•õë×ïL‘™ XõõõºµÙ YVò¼óÎûÙ¯~õ«ç 󷎯5‚L*]à§Nu={Û²}p—•«|èìùq¨&'ªr?ítuHøå YAËG¥yD6"q.‹jëET¯kÏ— \Ô†ŸD°íT"]w|^ûè£î~õÕ×_ûè£?]ç8i2Ð?ái!…ÂñžsH ·)#™maþº(MbUÐ*O>XÞ8c¯˜(è„4HŽÉï±(s÷©¤^ô@þñúøtww¥Nš9s×£ßùÎ Ñh/H¸W]uÕÛ·o?ǘ4ˆD"+V¬xvëÖ­/—zp‚äúyj"Pev:±®²m{û~nÈ$óË߀ 8™çíE>Tðº¿çJ{Q§É‹éç d:ô;ƒ-{¤z¸ÌCæG´-j'ó#²= Æ÷È#=7cÆô÷(¥L&a`` {GÕL.¸÷œyî‘©µ!v…u£>(ç¸>ù¾ó Š2ú›ÅŒÑâÊ,È?K äólÿ¢íÂ2’7.‹ˆÆï¯Þ²,…lhiiÖÖp] ee‘Þo¼áÿ^tÑE}Þ»ø˜˜‡{ÓM7µOŸ>ýÏ`@MMÍ[n¹åÑ­[·nšcÇ>¦âƒýŽT}‡j¿S>uíLü™ÚðÛü³ªpå Øn›÷%²c÷EíUýˆÚˆöem²Q6‚+Ê o9H êH5»ÍÖS® ðh4oËæ­{oß›U²uì˜øüxÑqðÇÂŽ‹÷Ç׉lùãEõ ŽýÚk¯=ÞÙÙùÝ|ðöõõÇ ®®,+¡O»ª/úTG­umdí3Û£ö:þûS·õ•:cP/;fYõ¶7q± µµ öïߎ“‚P(4tñÅ?ùè£ßÛ“5Ç®)Bmm-œ~úéï·´´\L)Þ¨.÷œ}öÙ›6lذéšk®éLA± ÓÅìǤLT®«¼H‰cþÙT°Ëêek½‹ÊTõ<²ãôûÚàw^ ~!1°H*!*Á:Á®úe}ÈD³ªŽ_e†g'š€ð~d“Ñd@d'¢`Rt÷w´ïß¿ÿ?õ“ŸüÁ“ãq ‹Å ΤÍÉS®^zQeÖÖ5i¤¿ yåʱHÆ`*Ðue–eëºÐ|ø46ÇIeÛβeËžÞ´iÓf@¦"îW\±{Û¶m=©Tªž­°m{pöìÙ;Ö®]û↠>ª®®–}ù!"F¦ ŠõwdêGe§›.²m*ØEe*ñí•óÓ‰€èXT>T¿^x×ÔâA²÷F™EÑlÑë³4vAÚûÙç·Ùcá×yçU´Í‹}Ó2SȺuë–>ýôÓÏ´, ¢Ñ(LŸ>ÊÊÊòY{Ù¶Ê;o[7~‘å¸teõ¹}ϼmÈvÛ¶add„ææfH§Ó`[ÖÈÒ¥K7nß¾}cyy9~PMQ¬E‹}÷øñãË F;êëëÿ¸jÕª-?øÁvÕ××;=F9Á VØtBžŸp˜D¦UâY%æùœwQtÝçëÙ}Õ]Xe¾eõ²1¨ž0]¦H°ÂÀ\´äße5¨0çý©úA=¶ƒ<ƒá¾©IÐ~íÚµ+6¿úê½ñx|>@4…Xm-TUW!$wѪ0 œÙÉ/SD‹Y{‘­È_þòüeÛ˜úÓö§ic46Õ˜ø²ìäÉË»ïëëƒ?ïßÇŽË^›`œ{î¹Oÿê•W~^__ëµOqV¯^}åûï¿ñÉ'Ÿüþ…^¸ëþûïo̾®ø„ ÅC/E?~êüFÝùm•PçŸuÂ]$¦UBÚ±/Õƒm+ó+›ê¸Q´ÍáÔ‰a•°ö#ÄuÂ_ÕFe/{‰t‘ø6ä2;*¨g·ù™¾ òío{áã?úÑïìêº Ù­¬¬„X,g”ˆK‘P¦ªzAÔY&®óê} e¥0fÆDÌ«Žß­î\åìH推CCCÐÜÜ ÍÍÍ022„ˆF£í«V®üç_mÞüZ¶)~PMq:::B###dþüùºœ œRwÖÿ]ÌoËúÖ•‰„¼LÜ«"îì¾LÌ›î›s?íuc‘KÁ6Š÷âÀ w¯L'–u¢@q„¾j_46ð±¯Ú6í*[ÓöB»_ÜT÷Àß¼­©éЗÒét€P8 555‹Å ‰ä.^Í[:R”"x–•©ÚË·i™Î·Ÿú ¶Þ/–eåÎu2™„¶¶68|ø0 @:˲ܺºiïÝxãÚ?þøã{AñC©…{Ð6~¢ðºr“g?x]êŒßˆº‰°õ§:¦¼mîÅA&ÜÌÒStÂ[%úMĹÉd4u2{Þ†·õ¶½èö[¯À÷÷÷[×\sÍ•ï¾ûîÍCÃçxQöH8 µµµPUUåååyË%ÊD¼2"n(nƒâb—ew2/ˆ¨žIgaËò^Tƒã`Åz:†øÐt?ííí¹õÙ)¥‡{/^üÒ÷¿ÿýŸ_vÙe}€ ‚L$¢ïV`TիļnÛ¤L%ÖƒFÝä©2|H¨ËÒtDýðõÂcEÑ^H´-3ñò…³Wç'¯‹Èë"ð¼O¿kÉ£pŸ@XážÛ¹0 o~Ÿ·W }Ó‰n,¢gÞŽ¯“µùà "ÒEeº72;>ºaÆEÏ?ÿüU---_H$'yÂÜ[Ý$Cyy9”••A$H$¡P(A6M?ñöUâZäÇïD@æ;ˆp× {I?®ë‚ëºà¤R0<2###Ç¡¿¿††† ™L‚ãŒ.bYÖÈÌ™3·®X±âÕo~ó›ï/[¶lpýnARâGôñß­A£Id]d/›èÒITv~¢ñ¦‚ÝᆰoÑñä•¡p/žp·AœܳH”—rßÏX@`cR&Ú— ó "Þ¯ÈWú:~ü¸õì³Ïžôì³Ï^xèСUñxüôt:]ÉÞ$ÆÝ죔Êïx÷諒PJsBž­¶m{(‰khhxëâ‹/þÍý÷ßÿáé§Ÿžòê‹2A¤øùŒ6‰º‹êtb]TæW¸›lãì—žh_Ýçë³Cá^dwo[ÙÖ¥ºø‰Èƒm¾LÔ—èXLëARnª‹7âã?ŽüÝßýÝÒ>øàÌŽŽŽ3†††Nv'æ8N•ëºQÙ)Ô¶í˲†lÛî///?RWW÷çÅ‹ïùò—¿üÁwÞy|¢ˆ ‚ " MÚ˜ˆye´YQ®K=Q¥£ðé6~/^•Õ‰|ÉìEãµïÅ‚¸‹"צ‘nÐ {¿ Ϻ2ѾŸh»ªÜ„b„Œ d#ñÛ·o¯Þµk×´¶¶¶ÚžžžÊ¡¡¡òT*ʾ¾ˆBˆ …Ò‘H$‹Åâ K–,é^µjUßYg•€ÑŸ^ñCAdòô³Z—£*²­‹ÈûIQQ]PêçbS?i2¢}ÑØ…{±à…;@¡xæËDuAÄ|PŸ&Ï~ÊDûªr•Ø.vîˆ&ljÈ1úàAAÆ £“RöaZ$uFiçëuÂÞo.bîW°›ŒSÔP¸ ]*ûGË@¶Lµ ÿ‡'Ú–Õ³ðåª?f¶N4vÖÖëCV'«÷Ì,™?G2¿ø ‚|Ò`¿‹¡nb#j£î¢rÙ³·­ò&ý˜ü BVïë5¡”ïcÇ$Z$Êu¢ÝD¼‹ìEÂWõ†QM&X?¼ˆW‰oÞŸÌN'öuoNS!®úcFAO:¦ß{ňÐö&B]dg™×EÛeö¦“]™l¿Ô¿† DÂ]Ý• u€B‘Ì—±¹Âl Œ—«‰hÝD‚µWM$x¿¢ êMĹ â‚ R\tÂÛDlú­u×m넽©¨— x]{L”™Œ¶V¸ë"Ø|½ßè»LxûÁ4F„,Ú¯²åûÕû‰ºcÎ9‚ ‚”“èx1Úðõ*Qob'Ùº_áM"æºr•M`# õ K•‘‰tÕ>€\ÈËf¹¢È>ßÞ/¦¹ï²¾d¤úù(E:Š~AäD¥"p¬"Ô$òΖëD½Jä›îö¥8¿yÚ sÜ‹ƒßu¾UQj™­.²­ºèT–’Ã×ÉÆ¨»èÕô‚W•¨Ø UAäD¡TB¯Xé A½itÜÄÎä?Ñòb—P´^¸ëÒeØ}àõ{Ñ‚¬oÖ—(ÍE&ôAb£³ Sá\LÍ_€ ‚ ' ¢ `1àx w¾Ì϶*ZoM×EóA)%(àÇŠŸˆ{Q/» T–û.Ò*ÑíùQõËd¢a2)懊G©ÊBAÉLQ„£ÿ õ"?û*q.+7í¦ãQÔó‚½8¨V•Ñ•ÉêeÛì¾Làˆß(*ï•«úUWUæÙØ1ZŽ ‚ G)£ô&¢ØD¸óûªh¼iTŸ·Õõå÷<ÛcŽ{qšã®ɲ;Hû‰–³ÑvUSá,šHðu²z•O¿iC‚ ‚ ßÁc‰ÄÀñ¢2•°7ë%Ñ,(ÞÇŽJ¸ËÄ®L¼{¨D´jU]Û^µÂ¨Óœ|¾?AD8FÛAdâ*MÛ5êÎïÈû­3í[5dÑEÜMS`tmDbŸ·—µ0› ˆ„·lb¡zÓªD¶_Ñ^ŠœwAA‚SÌt Qy?i.:•½ ²è|P¿¨wJŒé:î²ra/k#Šxó‘u•æûQ tѱ™ˆlÕ˜Líe`TAA‚Q,ÁXÌ”š ©4A¢á²ö¦i6AA‘>xÂÝ4‚®«W ~U„\U¯»˜Õ$ Çd•LÓjü ;Ï‚ ‚ä# Ëw1lÇš6£Û7Mg1íc‰Þ#ã€éÅ©¼8g1Y_ÕÏR‹ª4“œz•¨– c¿åÅ~CcJ ‚ ‚ø§”Â]էߺ ÑwY™It^%ÒýDâMƆŒ#A.N•ÙùI£‘áGÀÓÖä—ÞW±Áˆ;‚ ‚ø§ÔQb¿AmL…¾i4ݯ`Wµñ;Ù˜"B(¥è£Îª¼pS;Yꊮί­¬?mLý Y*‚ ‚ rJuŸè¼wU[¿bÜÔÖO_¦¾ðFLc„¸ûÉeW!KYÑ­þ¢J‘ÙÊ-'ÉÖÉi/õ›m"~òCA©Šß(p±ú«}HûXÊMü›ˆv?¾‘ÀFÜóÊd¶ Y½[S›REØÇ3ÒQuA) ã-"ý f“v&:ˆMÐ4?6ÊrŒº'¨p}?vA„¼ŸúbµAAdòSÌèúXm‚Fáýú2ñ;Ö‹lQ´‘b wQ¹ßȺ;¿¶*‚Špï‚ òɤâÝ´ßÕ\üŠö “b¤þŠ÷± î¹rU[?é2~ËLëƒÖ•ü‚ RZ&RM ÚÖpöe×ÉwN< ÷àø½sªWL½l%Õ‹"ºSvQæXVZ‘!È ÅÛø†EA©E1#êAÚ•:Õf,ý"ã„q'à?Ú]¬›•êBÒb mŒ#‚ ȉM±ìX¢ò:¿¼”…0âÝSýDÞMËù:Q¹(/ë_„iDÝTã AAJ_ê%"ƒ¶AÆ]Ä=ÏÎÄW‘Ú™´K|¼"è©GAÉÇd[ó=ˆbçÉ›¶S{Œ¶ þLêè¶îMÅŠœ«Pý c¼n¤äoNAA&jyI{?~0?ÁˆRe‚¤ÇÈìx[!/³µ3}ƒŒ‡`Çè:‚ ‚L]ÆCtNô®A}¢`Ÿ$YUÆ« o+²½Ð~}ÊPMŠMUjA™<Œ÷÷x1û+E„¾”~‘€°9îJS_AÇPBûR1YÆ ‚ Hñ™L‚´Ôè›Ö ¶IDAT¼ië»Ìo;&Â]Ú®„öãío²÷‹ ‚ ÈÄ1Q‚³TýŽ[ÊŠõâT¸ ýL@ÛR3™Ç† ‚ ÈÄ1™éXÇV”cCÑ^|r´â=Ïß$ó… ‚ òI¤Xâ¸h"{éŠã" ù@}O“i,‚ ‚Lm&“p-éXP¤/ë8ûb2•ÆŠ ‚ È'—)%tQ˜O.¦Œ bAA¢ƒBúÄæÿpˆ®k@IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/transparent.gif0000664000175000017500000000006112203711415025546 0ustar takakitakakiGIF89a‘ÿÿÿÿÿÿ!ù,T;pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/favicon.ico0000664000175000017500000000257612203711415024654 0ustar takakitakakih( " A>;)'%+¯ô=¿ö420520ÿÿÿ;96¸èú „„†µäô—àúGDA/-+LHF+©é\XUtØùååæììì,BO/Vi531WSP+®ñ·çø§§©OÏù--..-.QNK       #!!!!!!"   $$$$$$$$$$$$$$$$À€€Àpyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/pyramid-small.png0000664000175000017500000001560412203711415026010 0ustar takakitakaki‰PNG  IHDRÜ27µsKIDATxœí{˜Õ÷?çTU_æ>0ÃÜp„ŠD(ÞˆšhVØ,."»hbÜUß}³kÖ˜¼‰˜ˆ»„}ˆó¬1’Å Äý0sÙ™°íÛ„ilÛÎe·Yá8yIkkûÅÀÚS=ž!|ú‘3ÂM™2%lãLU6(ÇùDm9®p”"³œÌ(¶Efa.¥’6\Åá†äŒp0,d;#lÁ'òH:J H CÒÞÇ02Iôd]ZŸÂWì(gì€4„Ó¥À?Åèé¿ùŽ]Ü|›àù\ ,g„Êr‚*'¢N*<òxéîŽs×-—pVþFîÌA ÙCx)ï<Òy'2 :GåŸÀ ü°€¿ÆU@!tÞlÞ~ ´°3 pp!ð$ðǶ栟‡"•pïsÀ€CÀ†ösÂÈ¡ ׅ턲xiDF¡®&¨ô]ëìŽ3ãÒÑÌ›y>Öá5Ì»Zò‹W È )"y~z[‚äQá#¢Ð»Ž°Š[ü¨è£^#ð°| !¸ øw7ÿ9àR kí(÷¼B šv¬ 8þŒ–rÛ2Ø §NÇq~#|*±Ÿ¬ãQ)w¨«-áþ;'aÒI¼[ð÷×·°iW˜?n1´A'ÈàyÑÿ ýÜîG?«œyñrJ8”Êê¼H?iDâ´PÐäŸîºˆª2I¬³ åHò’n>ÎKÊ8Ö¦!ídÜ Met‘èã$`#°8­l!pšdÃÝýÕÀ®“Õé§ «€ï¢Uô:©Ÿãî6]ú :d®:êêòø¦'¿”)$†ÔI ‰”Ú¢“‘ØÚÜù7ã˜òÙâm º@YÄ¢ÔU:üãM],Ó41M Ë2±,ËMX@ ‘‚ Ë:Yk8³-"ø/à{n¾˜y2:û”¢X€¶±Þ8µCäÔ†ð—x:ãæ}RÈ'á¢QÅ5Ó«¸ùújâ](倊!”ä‹J®šàÐôyÁò5‚Ád[Bÿç 3‘Ü÷õ§ð ÃýÀKh½8Ï-@pÜÔ—Zc‘T±¼7³ ù ã$£$Unj"Ú©BK\´ûúè[ºý¨´~*j´$Ú Ä2œ[áÖ‰¢m¥Þl2ÿõô瞘À(´—±­9Ä8VUdAÎ$œ‡„ä1MÌDÞ hi¤%Q€` €”cÎ.æ¾[ë0T åDAÅ@Ùhs( *;–Ï-W+.9/€4B„BAB¡¡Pˆp(D(&‡ ‡CÉ”&‘ƒçÓt¸yÏN OëÑ^̪^Ο¼ãÖ½ßW~6Úíý.0ÈC«µ€ßS|uÇ»åÝ:ÞCKáK{éÿónß«Ð/:àhßïÝck/øÎ©E«…ï»ã[öØÞÜK?ån ©d‚Üâö¹ÁmÿOÀ:·}ÅiJºœI¸®®.By…zU—I)¤“ôåµ$Ï’üÃWë(+5ˆEõ‹U @Ù(Bèû.‰c0wÏ<ô\˜Ö0 é“jÚn“R‚ÀuܤØö K7€2ôD8èÝ´£åFwÿ:àçYΟ&Àƒ¾r‰&cMØ»ù¾ã–»À"·ØY£¿®D»Ò_Îи--§cÐ/‘(Žo½à¶µxh@K¿n·Î…À³èøØO3ôp¯³íAÌ„°{î-¾²f÷?ç¶ÿS´$>íCÂ4$á~ÁûÉ%¤&‡ô•Åm˜?³Œ có‰wÛ>w•a¢dGJÀ ¦$5e‚¯]àg¯0M‘ôˆ¦lÜr¡ çÄAõ7héZ"xx¸=碥Fú€ÂèI ðZzˆ¢'tM´Ùh/>FK2ÐoûçÑRu'šð!’®ø à_ÑRèXÚÚÝ­‰žÌÀ{€•nÙW€oºc]„&b-ðÀ¢oý_ÿÏísðŠ;?Iu9a~H’loßGKi&ôCÀh•ô´CNm8SJòóóQ(—\®Äq&š|‚X¦_Ϭ«†aÇHPàDAuC¼:÷ãÄÚô>1Šna0¹\°iT9O¯-¦¤P«–yyyäå… ‡B˜–…ÿc×h,“Ù1 db®DOÆ{Ýý?¯ùŽo~̦¢Õ¾ô7û…Àùnþy’“’vŽ7¡ß¾Œ~ã§ã?2”Å€_£¥ãOÑiÚæôóÛÂh"ÍJ«ó-´:øUà³nÙͤÆ¢_,¢íÇ™À’ cê £ÉZ5þpÔwü5´:º ¸èÛÎ rªR†AAA>Ji/¥!µÄѤ8JRQä֙ŧ 'v køa°£œã`·c·´ â¸î}´àÐmÀ_6ÀËkjùŸwBäA¡`‚‚JKK1b•” î滑Æ¡WIA«rõÀ ´ªÚ9qI—´‡¥è FOÂtÂ} ­;wÐsù‘ßV‰¡%J&²õ…U$%ÞzÎoý:ÃqÐÒùw¬o+2ÔY†–Œ…èɉâ«hõÖF‡Žf¨sMîœ,Õ:Qä”p¦iR\\Œã(M0‰K:‰eš!‰Åº˜÷ùc«Dìû (×ß ´ú®ï$†–!.Ù”Û†°?øë=ÌyìZ"6í´··ÓÜÜLãæF‚ eeeÔÔTŸ,)7½) -ÅîG;ÒñÚ›Xü5ÚéáÅŸBhÂv4fhÛǤª«½¡ý‚¨GK›*’o²>Îým–òÝè—I)Z•Íä´øØ&\_«sÒ"éØÙ‚vdûîXŠz©sJ[ÂY&%%%8¶:îf¹*^KK ï°™[¯ÙM}É¢‘ˆJmL9¨x’pB¸«T\òuÇ¡®|y;w.«G!É€mÛ477³¿¹™x|À„óOª½h±­‚BÛ\¯oõÒÆqô*”ïç‡B¡©kQTT49‰Œ(**ZÞÚÚ I 1}úôð[o½%”R˜¦¹?‹¹7,#„eYÅãñ»Ð^njĒRJÛ¶¥Âoù¯³=ý]$í®löW”¤·6Û8³aI’~HïAñn7vÈéJ+`QZZ‚m;˜¦‰aìÙ³‡õë×óç¶ñÅ©qæ^! €ö8_…a$q¤ vâ K!=Ò è´áÚ±ðw—·ð“Õ•„ƒ*ÕQC2‹FûíBVJy±"ãöÛo/xê©§„ã8äåå½ßÞÞþ÷ÍÍÍp8l{±+ íy3}Ép“ ˜Ë–-;úµ¯}­;‹îCO,9~üøÙo¿ý¶QPPptݺu…hÇJâJ}ôÑ’3f„ººº¨«««B;g2EΜ9órÇqn¡P¨«®®îƒúúú=ÕÕÕÇÚÛÛ+V¬¸!‹'NœØÌQJż¶¾ûÝï6,X°€ë®»nüÊ•+g ÉG«²öO~ò“a÷ÜsiÛ6åååE---gùöOÜ[Ì›ÛòÉ ø–u¹a˜+ ”*GÛAôà ¦% ýÀ-/Ýwß}…¿üå/ó#‘£F Ì®¬¬ôŒÂô‡îßO™LóçÏ<òÈ–ÆÆÆñ;wîœÚÔÔô?Ѧ¦¦K.¸à‚??ÞB«bÞùÂ4Ͱ·HÀ0 m¦;oŒGyäÜ—^zižã8Œ3æ£'Ÿ|rÅå—_ÞŠûzÚ¼y³õ«_ýêºX, ‡Ã^ˆÁ›üƈ#ª½ÆjkkkÐö—w\râĉE–eY¶msî¹çŽF{L=Ò*Àž3g<ð@éáÇ©ªª*ß·oßL·N7ÐýôÓO}å+_1lÛ¦¸¸¸àرcUî±ØÅ_l¾ûî»Þµy÷!NK%ä˜p¡`ˆa¥ÃXµj+_y…ÖcdžI~ždñ½6UÝЭ0Œ6Ž·Ei9;÷±«¥‚ãñQXã(¯¾€Q]€yI€[7òûèÞûGÂ]pV~+£J5 ó `€aÀÿr7óžj ù¸‰e$%›”Û¶©®ªªFÇÃ|2²TÚ˲‚ÂñBÏ18ƒ9kÖ¬w.\8þøñãÅK—.]__ôСC#¤”ö¼yóÞuÛõ·-ðM,¥Å†M†É¶lÙ²KÇ!G–.]úÜ´iÓ"$ #Ž;–ˆþ;Žã¸Ç„r'!ylý¹~”Ti$mÛöëåÞêïå#Ca†!B¡P½J$ÑFCCC¡GÚººº³Ð×(`¯X±Âœ0aB<‰PPP0¹±±qFMMM­¢v¢UÚ. »¾¾~øÖ­[½0ÌiÏ)á"‘O>ù$o®[ç®›4±Å?ÞjpNíqÞú}ˆÍE4·Uc„ÏeDÍxªÎ=ŸË¯:›êš* óSÕþ‹'}ø[:£°¿ùû÷î¢i÷G¼½kÑ)ÛY¸›1åðÀuøÖ‹µØŽp=¤Ú;ª”ðTR…ž$ý ˜ Û¶mw¢cÛ¶·ìi oWçî»ïþð‰'žh>räHåo~ó›q›6m:¨”ÕÕÕ»n¿ýömô\=!DZ=õ, áľ}ûÌŒ6lXË´iÓ§µc´´´Xñx<‰G ¹m½:@@‚éäWJ©LäOyùH)½c¸õ£¾±Û¶ƒi×ãõkÔÕÕ‰ššš=åuË—/¿æ›ßüfIò'^cÆŒ¹rëÖ­ùeeeE<MÌn·Ž§æ:îõæ 9#\Ie%ï½ÿñxÓÔÝÚŽƒ)b<óëc¬Þ0‰Ë®šÅ¤K§ñűc>¬ ßm‡pÎÈ ÎYS?ÜJ¤#ÆÖ­ÛøÓ{ï²|í*>n\K@Dé6ò0¤V-…”H¥PN<}` ”R‰ Õ›„骫«™4iÒV­Zõ¥ÆÆÆqÛ·oï˜:uêz˲¼ñeퟤtM!\ ˆI)€¶¶¶Â––9bÄïÅ gÑ¢E—z„s_)dqI¦Ü>3Îñ% )QJ ¡p'ç¶íÒ»f¯Ž1sæÌ7/rG.^¼xæœ9sÖÖÖúíDV¬X1bíÚµó¼>ƒÁ`:Fè‘-%¹¶j‚€ 2 sF¸cÍÇœ²ò"ýC¬Ž“X¼W»óÙ¾¿‰ ü˜Â¢§(/+㬳΢¡¡††FMMM eeeX–•Ò®mÛ=z„ææìرƒmÛ¶±uë6öíû˜Ã‡ÐÖÞN4'f—0%–¡5Fá~­ ¤ÂQŽ7ÉNáuûí·¿³f͚Ϸµµ•ƒÁ¶¯ýëëIÚBéý'ÊôïÙYY™ª««k:|øpukkkùœ9s¾´lÙ²ÿ.//·×¯__ðï|çêµk×^#„PJ)F ’݃\÷¤]õ6‹´MW¯ÝωS…[&üª+É ¾÷\â .üà /¼°¾©©iòþýûÏ4iÒ?Í;÷…/|á ÛºººÔsÏ=7îå—_¾9‹ ·E"‘Ññx\’$˜§*§ýâñ4¡”b°H7(„Súï„Ð+ Š€â+VŒ¹çÞÿkÚ¶üM€’pPûö£±(‡¦å@ lü3+W¾Œ”’P(Dqq1UU•ÔÖÖRWW‡eYìÚ½›÷îåÀ´¶§;ÚR`¦e%<¡†!±,3é™ô–’I‰£Ç„sǶmÛ°m[2p•€Ù³güàƒnܼyód€úúú÷/»ì²#dVsE<·Çé«çÁ|iÞ¼yãÛÛÛ‡½ñÆ_<ï¼ó&‡Ãá¶£GVÄb±ð¤I“VïÙ³ç쌎D"y¤®»»;q_âñ8î1ÿ˜„mÛŽ7–X,&HµqÇ(¼ûå!eâ»m˜nÞõxu ž}öÙŸÍš5+oß¾}çµ´´4,Y²ä[?þx»ã8†mÛ!!Dlîܹß[·nÝ‘Hdt4õIŽo›ž÷ۜ괖pJ© z`¡›ŠÜF{÷ À>|øp@H©?PH,,Ö;©.{76—táC{G[š¶Ò¸y Ê•Ò00Mýí›aš¾u’Þâe\{M¦Í#œr{`„ …BÝ•••[#‘HiUUÕ>!᫦¦fÏæÍ›'K)ã·ÜrËëøÔ¥ôþóòòºªªª¶tvvTWWïÎÖÿÌ™3÷?öØc>üðÃ7îÝ»÷3‘H¤¬½½½xøðữ¾úêÕÏ>ûìÚ+®¸b¶ã8”””"Õ¶r†1bD ÊËË»ÇS$\QQQguuõ–ŽŽŽ‚ÊÊÊýn”±H)©ªªÚnšfgUU•÷)M¢üüüÎÊÊÊ-]]]ù•••“êåT€š|ôèÑ‘#GŽÜ°k×®GûÓ ô/õÚk¯ kjj*ª¨¨è¸æškƒv>Šh4*ÂápÒzýd:–^Ç0 2Ž¥:*JÛ¶q¯ÇëK‘Û·o7V­Z5¼µµÕ;vì¡n¸á š@ÑX,fwtt8¦iÆòóóÝ·ÄHÆýêeBuM øŸtd•p®Zh‘T Ð’,ä–ëoc4¡l´Qêíû€¨­­mµLëx4Ë—=ˆÖ“@é_ϳ¨õP„Éã25ï'\*Ù¼2½oÑh„â’â ýœè‚‰¬0nºé¦/=zt¤+Ý^ìϘN´ÿk¯½¶åÚk¯=HÚä5 ‚x?Ÿ ŽÊp<= ã'œ¨sÎ9ǹãŽ;"$½Ëžªk[–/..N'U6©Ö#ä3XHN)å-T  UAO%´H®ðts…[!± *‘2®¡¡Á.--iÚßÜ\•bOùåÿb@¥ÌGL™~®¿<-Ÿ$o²=Ó09~¼•˦Oû€Ìމ\@òú믿výúõÐÐа桇úà'×È6É{#\‚tô´ËÒ¥V62§ª¤Ó%šG*oëý>‡§§§_¤G8oëOôüxyãÊ+¯|mÙ²§§ç…ót°8Eí“Y÷¥ï«Dfòô8?퓟Ôvô:ή®Nº»ºwÝ{ï½ïqŠÖÞ½øâ‹åßþö·oÚ´iÓu€(,,ÜõøãÿœT;êLB5“ž¤ó“ÆŸÒ‰–t§DºARjy¤ð_eûŽ¥í MJ?áü«4R¤››â?þñ÷ÒÊ•oµ·µMËÏ×q¶LÒÍS“v˜ÌRž‰¬ú‡aµÓD¦J7Oõt¸hçÎL™2uY]]]¹—&béÒ¥UwÝu×â®®®r€¼¼¼= ,øþ•W^yøŒçtA&ÂyÛLR.pžzÙ›'ÒOºœBø$œ§z‹j yʤJú =É'Ÿyæ™ò{î¹gq8™Ÿ_€B%m-™Ý.“=ˆ—J ÏÉ"S—FL©LISÓ€6mÚ´„S#IÄ®]»¬qãÆý¼³³³fäÈ‘¯/Z´èg³gÏ>“É'N¸tâe’nqúV7=$I™$ÉÖŸ”N¶L¶›Ÿtþ­\¸páÈY´è!Ë4JJK°L+%4 ÓHÒ“\ÉV{ÔK'«ŸhRÒÞÖFSS–e=¿aÆ%………§äMçÝ“ùóçO.--íX²d‰÷·ÇÏ45²¯ëM'ôT ³IºtÂõ©fæ‚p‚äO°ù¥œ‘a¿¿Ž’Þ _}õÕ‚o|ãó[ZZf†B¡a÷ÃÔžÒ)Tž=—‰l’¤C¯ÂhkoçСCD"‘M&LøÅ믿¾zîé‰Â»/gÑ<ôvÝ*-ß›¥/ÒeS7’r°C$œ'á¼”‰\þ2ßçžþ9è•h™>WK–,±lÙ²©üŒmÛ%êáËûuA¢ghÑqœx Ø___¿áG?úÑúóÏ?ßûíÂ3u’ÿoA:á¼mÔËÞV—du¢äJÂyΓtÉ•M}Ìd³ù½’™H–-ÈžéÛ±lèÍHÿmˆhÿ{)<àm{#œ—O'WºÝ–SÂyq¸¾.‚´ã‰•æô­J¦çéGùÎ<ôóçûCºlªf¦ör?ἕÜÞ@=ˆ u¼ûk³ nýÁÉ ]ºÔÊ{Ëiü̓iWú<Òù ç»B´!r áD‘-DàÏŸñ2.§È´–Ò?tÕ1lô²MÏÓò! úç¹ì¯3[꫟AA& çå='ˆ_ÒõF¶L$êXC¤B6œH¨ SY ØŸþN*öÿ½´þÎÆÕ;«t.k~äí!;?ÀÛŸîZ¢Ôq‚–ÜYÁ:ßîw·óuËmMŽ|¬<äÈì¡ñ+3ëÝ9ðípâ-nù³V=F|˜IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/ie6.css0000664000175000017500000000136612203711415023724 0ustar takakitakaki* html img, * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none", this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')", this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''), this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')", this.runtimeStyle.backgroundImage = "none")),this.pngSet=true) );} #wrap{display:table;height:100%} pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/static/middlebg.png0000664000175000017500000000535512203711415025006 0ustar takakitakaki‰PNG  IHDR¬º—ƹ CiCCPICC Profilex–wTSYÀï{/½ÐB‘z MJ‘z‘^E%$B °WDWiŠ"‹".¸ºY+¢XX°/È" ¬‹«ˆŠe_ôeÿØý¾³óǜߛ;sïÜ™¹ç<(¾BQ&¬@†H"óñ`ÆÄÆ1ñÝD€ÖpyÙYAáÞ?/3u’±L Ïúuÿ¸Åò a2?›þ¥ÈËKÐBй|A6å<”Ós%Y2û$ÊôÄ4ËÑQV•qò6ÿìó…ÝdÌÏñQYÎYü ¾Œ;PÞ’# Œ¢œŸ#ä¢|eýti†å7(Ó3Ül0™]"ॠl…2EÆAyJò,NœÅÁ24O8™YËÅÂä Ó˜g´vtd3}¹é‰„Âå¥qÅ|&'3#‹+ZÀ—;Ë¢€’¬¶L´ÈöÖŽöö, ´ü_å_¿zý;ÈzûÅãeèçžAŒ®o¶o±ßl™Õ°§ÐÚìøfK, eª÷¾Ùô Ÿ@óY÷aÈæ%E"Ér²´ÌÍ͵ x²‚~•ÿéðÕóŸaÖy²ó¾ÖŽé)HâJÓ%LYQy™é™R13;‹Ë0Ybtëÿ8+­Yy˜‡ ’b=* 2¡(m·ˆ/”3EL¡èŸ:üÃfå Ã/s­æ# /± 7èù½ `hd€ÄïGW ¯} $FÙË‹Öý2÷(£ëŸõß\„~ÂÙÂd¦ÌÌ ‹`ò¤â£oB¦°€ä¨- Œ Øà Ü€ðÁ ĂŀR@ƒ\° ¬ù ì{@9¨5 4€ œÀepÜ}à>#à˜¯Á Axˆ Ñ 5H2€Ì ˆ ͇¼ @( Š… dHI¡UÐF¨*†Ê¡ƒPô#t º]…z »Ð4ý ½ƒ˜ÓaMض„Ù°;GÀ‹àdx)¼΃·Ã¥p5| n†/À×á>x~O!!# Da!l„ƒ#qH"FÖ H R4 mH'r D&·††abXgŒ/&ÃÃ,ŬÁlÔcŽ`š1˜[˜!Ì$æ#–ŠÕÀša°~Øl26›-ÁÖb›°—°}ØìkÇÀáp¾¸X\*n%nn®w׃ÆMáñx5¼ÞŒçâ%ø||þþ¾?‚C ´ 6oBADØ@(!%œ%ôF 3D¢щLä—‹ˆ5Ä6â âq†¤H2"¹"H©¤õ¤RRééé%™LÖ%;’CÉBò:r)ù8ù yˆü–¢D1¥p(ñ)e;å0å<å.å%•J5¤ºQã¨êvjõ"õõMÎBÎOŽ/·V®B®Y®W›¦°©iŠi…é 3ØÌÞLh¶Ï¬Çkîh.2¯6`QXî¬V=kÈ‚ah±Á¢Åâ¹¥¾eœåNËNËVvVéV5V÷­•¬ý­7X·Yÿicjó©°¹=—:×{îÚ¹­s_ØšÙ l÷ÛÞ±£ÙÙm¶k·û`ï`/¶o°wÐwHp¨t`ÓÙ!ìmì+ŽXGÇµŽ§ß:Ù;IœN8ýáÌrNs>ê<6Ïhž`^ͼa]®ËA—ÁùÌù óÌtÕqåºV»>vÓsã»Õºº›¸§ºsîaå!öhò˜æ8qVsÎ{"ž>žžÝ^J^‘^å^¼u½“½ë½'}ì|Vúœ÷ÅúøîôðÓôãùÕùMú;ø¯öï „”<4 ¶ÁAþA»‚,0X ZÐ ‚ý‚w? 1 Yòs(.4$´"ôI˜uت°ÎpZø’ð£á¯#<"Š"îGGJ#Û£ä£â£ê¢¦£=£‹£c,cVÇ\UƶÆáã¢âjã¦z-ܳp$Þ.>?¾‘Ñ¢e‹®.V_œ¾øÌù%Ü%'° Ñ GÞsƒ¹ÕÜ©D¿ÄÊÄI‡·—÷ŒïÆßÍ¸Š£I.IÅIcÉ.É»’ÇS\SJR&„a¹ðEªojUêtZpÚá´OéÑ鄌„ŒS"%Qš¨#S+sYfO–YV~ÖàR§¥{–NŠĵÙPö¢ìV ý™ê’K7I‡ræçTä¼ÉÊ=¹Lq™hY×rÓå[—®ð^ñýJÌJÞÊöU:«Ö¯Zí¾úàhMâšöµzkóÖŽ¬óYwd=i}Úú_6Xm(Þðjcôƶ<ͼuyÛ|6ÕçËå‹ó6;o®Ú‚Ù"ÜÒ½uîÖ²­ ø× ­ K ßoãm»öõw¥ß}Úž´½»È¾hÿÜÑŽþ®;+¯(Þ´«y7swÁîW{–ì¹Zb[Rµ—´Wºw°4°´µL¿lGÙûò”ò¾ ŠÆJÊ­•Óûøûz÷»ío¨Ò¬*¬zw@xàÎAŸƒÍÕ†Õ%‡p‡r=©‰ªéüžý}]­zmaí‡Ã¢ÃƒGÂŽtÔ9ÔÕÕ8ZT×KëÇÅ»ùƒç­ ¬†ƒŒÆÂãà¸ôøÓ~ì?p¢ý$ûdÃO?U6Ñš š¡æåÍ“-)-ƒ­±­=§üOµ·9·5ýlñóáÓ:§+Î(Ÿ):K:›wöÓ¹ç¦ÎgŸ¸|a¸}Iûý‹1ow„vt_ ¸tå²÷å‹î箸\9}Õéê©kìk-×í¯7wÙu5ýb÷KS·}wó ‡­7o¶õÌë9ÛëÚ{á–ç­Ë·ýn_ï[Ð×ÓÙg ~`ðÿÎØÝô»/îåÜ›¹¿îöAÁC…‡%4Uÿjòkã ýà™!Ï¡®Çáïó†Ÿý–ýÛû‘¼'Ô'%£Ú£uc6c§Ç½Ço>]øtäYÖ³™‰ü߯|nüü§?Üþ蚌™y!~ñéÏm/Õ^~eûª}*dêÑëŒ×3ÓoÔÞyË~Ûù.úÝèLî{üûÒ&Ú>||ð)ãÓ§¿›óüìÎçŠ pHYs  šœPIDAT(cøøñã& €ÿÿÿ‡²H#SØ4½ø¹8]E„¶A¢ÍTô¶àÄ&†ÍDˆaSB¬ë±9§%Hr3NÏ $,Â&…¨½´›IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/models.py0000664000175000017500000000123512210154301023061 0ustar takakitakakifrom sqlalchemy import ( Column, Index, Integer, Text, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import ( scoped_session, sessionmaker, ) from zope.sqlalchemy import ZopeTransactionExtension DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base() class MyModel(Base): __tablename__ = 'models' id = Column(Integer, primary_key=True) name = Column(Text) value = Column(Integer) def __init__(self, name, value): self.name = name self.value = value Index('my_index', MyModel.name, unique=True, mysql_length=255) pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/__init__.py0000664000175000017500000000106512203712502023343 0ustar takakitakakifrom pyramid.config import Configurator from sqlalchemy import engine_from_config from .models import ( DBSession, Base, ) def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) Base.metadata.bind = engine config = Configurator(settings=settings) config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') config.scan() return config.make_wsgi_app() pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/scripts/0000775000175000017500000000000012210157153022722 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py0000664000175000017500000000001212203712502025021 0ustar takakitakaki# package pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/scripts/initializedb.py0000664000175000017500000000143412210154276025750 0ustar takakitakakiimport os import sys import transaction from sqlalchemy import engine_from_config from pyramid.paster import ( get_appsettings, setup_logging, ) from ..models import ( DBSession, MyModel, Base, ) def usage(argv): cmd = os.path.basename(argv[0]) print('usage: %s \n' '(example: "%s development.ini")' % (cmd, cmd)) sys.exit(1) def main(argv=sys.argv): if len(argv) != 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: model = MyModel(name='one', value=1) DBSession.add(model) pyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/templates/0000775000175000017500000000000012210157153023231 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl0000664000175000017500000000670212210154276027023 0ustar takakitakaki The Pyramid Web Application Development Framework
pyramid

Welcome to ${project}, an application generated by
the Pyramid web application development framework.

pyramid-1.4.5/pyramid/scaffolds/alchemy/development.ini_tmpl0000664000175000017500000000262312203712502023571 0ustar takakitakaki### # app configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html ### [app:main] use = egg:{{project}} pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar pyramid_tm sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite # By default, the toolbar only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, {{package_logger}}, sqlalchemy [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_{{package_logger}}] level = DEBUG handlers = qualname = {{package}} [logger_sqlalchemy] level = INFO handlers = qualname = sqlalchemy.engine # "level = INFO" logs SQL queries. # "level = DEBUG" logs SQL queries and results. # "level = WARN" logs neither. (Recommended for production systems.) [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s pyramid-1.4.5/pyramid/scaffolds/zodb/0000775000175000017500000000000012210157153017026 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/zodb/CHANGES.txt_tmpl0000664000175000017500000000003412203711415021667 0ustar takakitakaki0.0 --- - Initial version pyramid-1.4.5/pyramid/scaffolds/zodb/setup.py_tmpl0000664000175000017500000000207412210154276021602 0ustar takakitakakiimport os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, 'README.txt')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() requires = [ 'pyramid', 'pyramid_zodbconn', 'transaction', 'pyramid_tm', 'pyramid_debugtoolbar', 'ZODB3', 'waitress', ] setup(name='{{project}}', version='0.0', description='{{project}}', long_description=README + '\n\n' + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author='', author_email='', url='', keywords='web pylons pyramid', packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requires, tests_require=requires, test_suite="{{package}}", entry_points="""\ [paste.app_factory] main = {{package}}:main """, ) pyramid-1.4.5/pyramid/scaffolds/zodb/README.txt_tmpl0000664000175000017500000000002312203712502021550 0ustar takakitakaki{{project}} README pyramid-1.4.5/pyramid/scaffolds/zodb/MANIFEST.in_tmpl0000664000175000017500000000020612203711415021615 0ustar takakitakakiinclude *.txt *.ini *.cfg *.rst recursive-include {{package}} *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml pyramid-1.4.5/pyramid/scaffolds/zodb/production.ini_tmpl0000664000175000017500000000207012203712502022745 0ustar takakitakaki### # app configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html ### [app:main] use = egg:{{project}} pyramid.reload_templates = false pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_tm pyramid_zodbconn tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console [logger_{{package_logger}}] level = WARN handlers = qualname = {{package}} [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s pyramid-1.4.5/pyramid/scaffolds/zodb/setup.cfg_tmpl0000664000175000017500000000103512203711415021701 0ustar takakitakaki[nosetests] match=^test nocapture=1 cover-package={{package}} with-coverage=1 cover-erase=1 [compile_catalog] directory = {{package}}/locale domain = {{project}} statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = {{package}}/locale/{{project}}.pot width = 80 [init_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale [update_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale previous = true pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/0000775000175000017500000000000012210157153020547 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/zodb/+package+/views.py_tmpl0000664000175000017500000000030112203712502023301 0ustar takakitakakifrom pyramid.view import view_config from .models import MyModel @view_config(context=MyModel, renderer='templates/mytemplate.pt') def my_view(request): return {'project': '{{project}}'} pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/tests.py_tmpl0000664000175000017500000000060412203712502023314 0ustar takakitakakiimport unittest from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_my_view(self): from .views import my_view request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['project'], '{{project}}') pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/0000775000175000017500000000000012210157153022036 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/headerbg.png0000664000175000017500000000031312203711415024301 0ustar takakitakaki‰PNG  IHDR4b·Ît’IDAT8í“AÄ C“\«—›SÏn`b‹íºà)4ÁoŸàû9DRp‘hµÈ¯«×uìô½?÷Þ²‡ ”@»÷¬\SÍ=ýÌ®Š3}½÷­¾ÚOÜÕÜåo¼±FŸ5´9,×cå©ïß»'çãmZó&kÌéä… ´q~øx²Xò5†èßE3˜,óçû÷”01]îsÖIEND®B`‚pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/pylons.css0000664000175000017500000001221112203712502024066 0ustar takakitakakihtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-size: 100%; /* 16px */ vertical-align: baseline; background: transparent; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } :focus { outline: 0; } ins { text-decoration: none; } del { text-decoration: line-through; } table { border-collapse: collapse; border-spacing: 0; } sub { vertical-align: sub; font-size: smaller; line-height: normal; } sup { vertical-align: super; font-size: smaller; line-height: normal; } ul, menu, dir { display: block; list-style-type: disc; margin: 1em 0; padding-left: 40px; } ol { display: block; list-style-type: decimal-leading-zero; margin: 1em 0; padding-left: 40px; } li { display: list-item; } ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl { margin-top: 0; margin-bottom: 0; } ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir { list-style-type: circle; } ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir { list-style-type: square; } .hidden { display: none; } p { line-height: 1.5em; } h1 { font-size: 1.75em; line-height: 1.7em; font-family: helvetica, verdana; } h2 { font-size: 1.5em; line-height: 1.7em; font-family: helvetica, verdana; } h3 { font-size: 1.25em; line-height: 1.7em; font-family: helvetica, verdana; } h4 { font-size: 1em; line-height: 1.7em; font-family: helvetica, verdana; } html, body { width: 100%; height: 100%; } body { margin: 0; padding: 0; background-color: #fff; position: relative; font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif; } a { color: #1b61d6; text-decoration: none; } a:hover { color: #e88f00; text-decoration: underline; } body h1, body h2, body h3, body h4, body h5, body h6 { font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif; font-weight: 400; color: #373839; font-style: normal; } #wrap { min-height: 100%; } #header, #footer { width: 100%; color: #fff; height: 40px; position: absolute; text-align: center; line-height: 40px; overflow: hidden; font-size: 12px; vertical-align: middle; } #header { background: #000; top: 0; font-size: 14px; } #footer { bottom: 0; background: #000 url(footerbg.png) repeat-x 0 top; position: relative; margin-top: -40px; clear: both; } .header, .footer { width: 750px; margin-right: auto; margin-left: auto; } .wrapper { width: 100%; } #top, #top-small, #bottom { width: 100%; } #top { color: #000; height: 230px; background: #fff url(headerbg.png) repeat-x 0 top; position: relative; } #top-small { color: #000; height: 60px; background: #fff url(headerbg.png) repeat-x 0 top; position: relative; } #bottom { color: #222; background-color: #fff; } .top, .top-small, .middle, .bottom { width: 750px; margin-right: auto; margin-left: auto; } .top { padding-top: 40px; } .top-small { padding-top: 10px; } #middle { width: 100%; height: 100px; background: url(middlebg.png) repeat-x; border-top: 2px solid #fff; border-bottom: 2px solid #b2b2b2; } .app-welcome { margin-top: 25px; } .app-name { color: #000; font-weight: 700; } .bottom { padding-top: 50px; } #left { width: 350px; float: left; padding-right: 25px; } #right { width: 350px; float: right; padding-left: 25px; } .align-left { text-align: left; } .align-right { text-align: right; } .align-center { text-align: center; } ul.links { margin: 0; padding: 0; } ul.links li { list-style-type: none; font-size: 14px; } form { border-style: none; } fieldset { border-style: none; } input { color: #222; border: 1px solid #ccc; font-family: sans-serif; font-size: 12px; line-height: 16px; } input[type=text], input[type=password] { width: 205px; } input[type=submit] { background-color: #ddd; font-weight: 700; } /*Opera Fix*/ body:before { content: ""; height: 100%; float: left; width: 0; margin-top: -32767px; } pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/pyramid.png0000664000175000017500000010043712203711415024215 0ustar takakitakaki‰PNG  IHDRî©t߸7 IDATxœì½wœÅ÷ÿ®ê ›µ «œ³–@QÂ`‚cls€qÀÙÆáî±§ó%ßó»óÝÙ>?ççüpNgŸ³}‡98°M8L2QH("i…ÊÚ4©»~|»gzf'íjvV»ª·^­éé®®ªîéíþÔ·¾õ-…eÄsÍ5×Ä»ºô´X“·H}®vÔ03ŒaÐ8Üõ;Õ1¥Q ”:f0¯*Ôv0ëL&ù|&“ÙýðÃî:Z,‹Åb±¨á®€ep\sÍ5ñLÆ™éjo¥6ꔺ˜¦E)eדÀƒR*‰1{ æicxÈdÜ?x^r‹ñ‹Åb±X† +ðFW]uÕ—èÕà]‹Qg+­æ*¥0Æ`Œîê*”R(¥0€qÝÃF™Ê¨ß×üìw¿»oãp×Ïb±X,Ëé…î#„s®»®i\Æ»ÁxÜœ¥§Àó¼a®Ùéˆxç¹cÌK(õË„›üÖc¿ýmçp×Íb±X,Ëéî§8«W¯ŽhÝx)Žú¸Vêu(ñY·Öõá!ðB2Æx 6»˜¯¶6œøþÝw?Ö5ÌU³X,‹Å2Êq†»–Ò¬^½z‚ŽÄ?­õJ©å@t¸ëdÉ¢”R ®N¥âsæÍ]°ãå—·íîJY,‹Åb½X‹û)ÊåW]{™ç¹Ÿ×J_ ÖÂ~*£”Æo†/Ä£|ã¾ûîKw,‹Åb±Œ>¬ÅýdõåW¾¯i­—yžgEû)Ž1­u›1fUÊõÚçÌšñøÎ;SÃ]/‹Åb±X,£ +ÜO-Ôe—_y‡VúËÚÑSìÀÓ‘ƒB2¦Ñ磜ö)“ç?ùÊ+Ûû†»^‹Åb±XFV¸ŸB\ºúÊÛ´V_QJµ+ÚGÆF)¥Î‹D¼‰³gÍ|dçΉᮖÅb±X,–Ñî§«/¿ò=Zó”cŒí#ƒAiçlƒš6cÚ’‡;;·Zñn±X,‹å¤±Âý`Õå—¿ÞxêkJéñÖŸ}´`PJŸ‰“Ò»vìøÝpׯb±X,ËÈÇ ÷aæÂÕWž©1wi­çZÑ>ºP œ5{Îìõ»vìØ2Üõ±X,‹Å2²±Â}¹æškân&óeí8WXÑ>:QJÇ ,;{ÖC;wî<<Üõ±X,‹Å2r±Â}™2}æÛµŠ|Òã` ì2Êc ZéIž¡cL[ë}Hc±X,‹Å2¬p&.½ôÊÀÿUZMµÖöÑü¾j~C,þRgç·»>‹Åb±XF&V¸jÆœ9wjÍ[l™Ó¥TÔY3§ÿº³³³w¸ëc±X,‹eäa…û0pÁ«kG}k­í§:Pú@ç®k‡».‹Åb±XFz¸+p:â8úv0sŒgýÚO—Åx­u#F½ó /œˆÅb±X,ˉ wN7Î;ïÒ9F¹×+ ÖÚ~:áyƘ3ŽÜ3Üõ±X,‹Å2²°÷:ãÄôÐs‡Ûl—ú/¾Õ=¦áº .¸ ‹Åb±X,–`-îuäœsΉ‚¹\iÝàyvPêéˆg<<¸(v¦/w},‹Åb±Œ¬p¯#±ÖÖY&mÎ6žÁJ=M1 `q$9+Ü-‹Åb± ë*SO2™ù`¦ZßöÑRªä‚Œw.òÍb±X,‹¥*¬Å½~hãéåJ«˜±n2#¥ªÐÚUõ¦˜×N9çœÆ}O?mcº[,‹Åb© +ÜëÄ9çœÓf±RϺɜҔç5ùåŒÁÀ¢¹ öAg-²´X,‹Å2ú±Â½N8ŽÓh0óŒ„îêŒZª²ˆW`0¿Ï@ÊõD¸Mg>V¸[,‹¥z•Ý,ƒxf–QˆîuÂÇc‘ ãÍiìÕìyiâÑÚ ­¨…P¯w™J)0F5ª’Åb±XF?qàÓÀ" Sd¿ú€ï¿`Þ¯ó¸þ§D€µÀ[€v`2I˜ Ü4Ow°\˱½N脊ÇmÃèÓÒâîy†hD3kj;^éAk(¥"Œ{-OF|ŸìïgŒÁ£=ÏL:©Œ,‹År:Öç•IÓˆö ÷‹€w#Öüqˆ`?‚ˆó€ÃÀç€Và àI`°øß~ßÁ ÷!ÇF•©™hFL#Æ ûD@ñôö¦¸ìâ¥üí'°pZо¤óùûUè_­ËVEþ ×u•­•blÿ;Åb±X,–¢ Q!MH "ïnÞ ¼„ö?ó·ýÐÊw)0iHœÊ#9ˆr-ÄZÜëD4ÑFeN·PJ)zúR,œ7‘¿ï ¦6üžO¿µ—5Fw/4DG¼ê¯ËÀ­åµ½æƒ±Öçc£”ñâ5«Ô©ƒÆ„–fÿ³Õ_oð—ÒíÆCü½ÀqàUàpØOñ®a‹ÅbLC\;3ºÇUzUã_Œ]þŽ"ÏãuÀ3þþK{ÛnÄ"¾¿~>2'ÉäA–k V¸×cÔh’íÕØD2Ø–>üîULÔBú•$ËôqûU­|õšT"N}ÜcŠQùÂ2|W £âv˜\̦ûßÇcýÏ&äeñ?µ¿”z¹xþâ"Ö>D´ï6 /’uˆ˜ŒUÉb±Œ¦ â5À,ä9sqù69Ñi©ž8ò¬Vô7®8À&äº_ <,~…üV¸×+ÜëÈHWiýh…ôžgȸ.·ß²’+VÍ'ÓÛƒç¼T”[¯H²}_#¿|TÑÚ8tu†…w¬FÈô˜pzE¾÷Ì(àMH7jóäÝŒ4¦+€‹ü^à!àgÈ‹ùÕ!(Ûb±œÚLA|ª¯,Ø>X\¼ Ti© bHy¸xòZ[ ¼~ëuZa}ÜëÉ)àk^/¿p¥}‰4¯¿d!7ßxn2çfC&1;nH°t–!xÅL)^ç¢õ¦túRªºTÞÅòLÉÅ–¼óÄü¥^Äëþ»+Ï¿!7;Ð×b9}ˆwÒ_´‡™ü-"ä-µÁAzAŸ&ïvÛ±Ööºa…{ÝH`ð0fx–*áÁ¥ ½})Î:c Ÿúèe´D!“L ÞH&`zGš?gŠŽvH¥ â9RMþô_ ?]±4¡´yé•Ô¹ßB°0 z•B)åï—ë‹7*\¶‡sà8Ò=ûMà׈EÞb±Œ~Î>TEºËç‚•µA#Ñfžzñ¾qOr†±^§V¸2Dö_{ì`èKdhiˆòÇﻘÉ›I§’(<0AhXJ“LDX>?Í7¸¸.d2¾¨.q…u*›.ï¼*,’ªÌR¹NZëŠi¤î³Œ¥†Dù¿´ ou,Ës)2Ƚ X‰u ¹^1òÅxÓ} ° 2öÈøûFcÀ…S{3ב¡ˆ(Óßï¼ú2NæØRxžÁó<Þú¦³8ï¬idzXîÏ€Ñ~yIi®½(Åã¹ï)C¤Qöôk2iDÖ7—$_‡S©ý¼07É£xcÅøÿ—lÌTÓÆñ{‚ÿ­|¯9mHø²iÀg_L‹Å2ú˜<€´“A™¢ºŒ6’À/w˜½¡í¯_žÿ,þ ñÿ<[ךž¦Xá^O©ÔÊZ¾KäY•µ|€õ©”§RоdŠË/šÇm´/Õ‡çy!˜àx%"±²Çã†;Þ˜b×[_æF¨8¦H]_w¥Šïƒ*¹¿|™¥÷—>°_(È~ô£ƒLæqèBb§—ip%ÄŠÖŠ„w›ÈÀ{p»Ÿ×Hw®Åb]ô m6”ì@è¾XdûVà#¡ï_-Øÿ§œnoµa ÷:‘œ"j´ª(&¡ãªu_HÈÄjy%´Vôö¦9cÁD>ó‘ פI%RAe±îúî2ûûV$ó¦»üÙÛ2|ükQNôb‘œx/W~©}•Ï©øõ)%èË)úbe•®—|c†Õ9¼Îüyf/ò &òH“ÿBÕH÷lb=Ÿœt‹Ÿ‡ˆùjPÈ ©ÈL6l¤Å2ºH¤˜g±Õ+Úë„îu¤˜»EwJ¡OÂÆä—WÓßU$X/Öû›+¥H&3Œçãï?—É“šHu'AûÖåÀÊL•jÊ„\ç”G²OqöB¾þþÇÏí¦ýz;„ZB$|7áíácŒÉ}/8çR×£’0?ÃÂîjðŒw: —Úü÷Ò ÷Ò=» ívÕGù2ˆê‡(Ûb±œúüx ¸¨BºÀ½XQiEØÁ©õ¤H0’“ ¹X.Ÿbyi¥Kî×JݯPÙ}á%||&í’q=>𶜿b ™Þ¤¸Çxžø·>ãùϼ¡£"â=M&i¸ñ’7^ ½ q§ —_x~ŽÒhÊH“· 0Uáº䥵ӯ Gk­ÑJe— 7­šÐä-¹¡pP8h%Ÿ*(³ðÚ>&÷“1àbaûðFànªëúŽŸB&f±X,£‡ƒÀ_P~‡n$äsu©‘ÅR'¬p&*Em f׬tü@"­”KS*oŒÉ-Åê $’.—ž;ƒ¯šI¥ð<L ÿè/›Œ ¬ðâæn”ñH{Š˜V¼÷ê ¦+™ü(1ýb½” SM„—ÜR,ÒLÁ¿øVJçÊ G“ñ×µ–%»ˆ¿eÐàIà½Àÿ¡ºÁfKK½Åb]<|qÅ;‚¸Ã$£ÀSHœ÷ïr:™H,§ÖU¦^$À89÷‹b®aÂn(åŒÿwv¿$ª~ß—H³lñD>sÇy4Ç • ´TN çLû®o€Ñ„Ý̕ր"•2Lé0Üy³Ç_~'ÂñC,šßn´ämÔºð<2ø´ûUä×/ûl)EvlRJgËö<‰°cŒ¡„›½¥2Kº |œòÏ1î¿B–Y,–ÑÃOwºyHÏš:]À¡a¬—Å2dXá^WLö3'ªË„¤|”“ʾÝʰè Êêó.Ü–L¹Œimàß³œéÓ›Hu§D˜~íá:d¨Ê&±Z‡2 ŽQN:\p¦Ë{Ö8|é?žHÞÔÙQ«ë\í¹ôObòEy òV(PA#!eÇ2\$fû<àÍÒ.®Ã w‹e4r ]øôpWÄb©ÖU¦Žä{äœ> ·«"ûú;Š”ö‡Ïù™ûSéþÇÙ–u)²=ŒÄk‡÷Ü|ç/ŸHª' x~eƒŠø“.eÝf‚™XµÔËàûËhäVÔ`4Æ7¥¸ñ¢4×®„¾¤øŽcü¥œÜ1h±Â‡|] ýñå¼Âî1¾‡zÖ¯]ƒ’|%oGòÒ¹¼ƒ‰–ÂKá€B—Yü<ý¼‚ßÔößž4ÝÀ(ïç bu_Mu¶X,‹ÅrÊb-îu$ì“s…éŸN…þï·ï¤¬Ì…å”4·—È+U%™ryÛ ¹åºyd Œçf9*M†`=ˆ#ªÕ®B1rüI™|ÿ2ˆD ¹Áãð±k7šÊœÅël ¾÷?§ð)«Ð9—¬?ð—ÉsÛQá¯è¢å„\¤‚ØBÆP*$¥e@üø7Äu¦KéÀ¶!¯‘Åb±X,C„îu#A0{°…ó‚æa(çÂRé»êç…HGUê 7ŽPB¥(¬b v»{3,š;–w¼e±ˆ!ðÐYŸö°XŸQÐuÍŒ\‡)¨“lO»0¾ÍðÁë]¶îuèê…x,—.ïS0& ÿ#”6×,!ÚÚOt wò¾ãl ûßk•/äà ­<<¿C^ Àr2ü'ðAd*îRt3±ÂÝb±X,#+܇œu·: zU~Ô…_Šì¯lu®à÷­©´Ç¸1 ÜñŽ%tŒ“î ÏkQ*ŽúW™¬…BŠØÏE!iP$“pÆxç•ÿï×"’#¡»¶”¥½ÔIî/tv ê×#Q&¯jz8Ä•¦_2Ëɳx¸¬LšÄþÁºÔÈb±X,–!À ÷:ˆZcrQPªî%¾©B³zx›*bËWR4äÆÑ/¿Âº¨À_ñ®7/àÒ×ú~íO{6«‚A©aK|Ð@ØÚNÖZžmÌ„º<™¤âÆ‹]vîpÏZˆFB5ÍZØs¶óP»¨È9…NÈÿPy×4\­°;S~š~ü‚4á²åtZñîKm8Š <-'ÜA\edÀ…Åb±X,#+ÜëDˆdµ¬ hŠ˜ÊýµBx“b2?ýõhQ”Î;¯ΛÂF„¡;áò–+gqËu3É$Ò¾?{vDlá®(ˆÿîùb8‚2:\P¿:øUE¡H»šxÔðÁë ûFxv47–䯄s,s] …{‘ߣŸp/Ù3Q覓ÿ]+…1^nÀ±µ¾×Š$þ­íXá^kÚ€9H(¾qH‹¼™õöÅ–Z)Àä·l%7à8 Gâyö#wZ€ÀT`¼ÿ]=ȹîvSݼe âb6ù}›ýí=È5î^RCPöh@#×mòû‘er¿îö’›1Úb)‹îuED¬vræðBw‹@ær”ý¡õ57œO^~…ŽÞÁÖâ:9›°_£@)ziÎ^2ŽÛß2›ˆ2¤\7dåœÀC"ݘ|!o‚m"ÜÊÅwT&Ü=*׈…Z)He ½Þ·þúǺ!í=ŠŸ²Ê¥)lÏ”s)Õ{Q˜¦Ê<ƒßW: ÂMZN–jDZ#å›K³€³(/€¢ˆP©Wè¹82­{ ¥g‹""jÕ+z "¶ ó ºÂ6/WÈc)p5°˜ø?7ÿ} ¾‘¤±¸88˜ŒUhD®KŒ`àœw0O°™„çidҮ݃¬Ç`˜ã×7°l„‰;*äÑ œ \ \èçÙŒœ{ðäK½HƒåYÄ ìNþ\Û•H$¦ó‘FC3òû+ûàà7Ôw ÉL`9ů3ȵ~©_53-× …üM¬.@ê8ùŽ“Ó]idð[Ð[ <<y‹¥(V¸×‰8É›0Hû‘Eòr1#4ôwù(eÏϯ=Љÿb;Ãù¤S†ö–8yÛ|&uÄIõ¦é7*3‹ µ&Â0Uv]a Ux»’%¢]‘?@V‘JÁÒ9†w__þ…Âõ  &³R¥Ï-lU/ÚK¡ wCjlå‰õÂtyç婵Î^3ãyYWKM¨Å‹yð¯ˆ-dzÀõÔÇBödöÇñeÒ(àKH„j¸øP™ýÿf³˜èž¼ ¸ NÅÞ!ÍÈ5Ì > X\ƒˆö¹ˆXuÊäÓZ_ˆ§$°¸ø6°uu(W_¦ôùÿòkd5—FÄs åÃ6·#–Ü%À[€—€ïßB⛄&à*¿ìóÈYö«)ûMÈX“Ÿw! §¡f rËñ ä|Ž}uˆ# ¶w ×qÒ¸¬†yˆÈÿ°¸ø&Ò£a±äa…û0 xà¹ï¡=%Ü. ˜5¸ÿäMù‘RBéµÊêcÝ23®°0/Ï3(m¸yÍL–-Cº/«]!ŸFl#”ÎOƒBár1äÃç›s2¾ˆ[°‘0‘¯;žÝá¡õ2¸T²*g9_ÎP'Ô¦(7¨U[è^ÓßÝ&´O4äŽw¬X"ªy–õQ>”Ï3ˆø½¶B>ËeÀÃUÕlð(Äâ9½Bº£ˆµ³ZëvÃ¥h*±}%ðUàµU”1PKû"亿±æW#Ô+¡+ñ2y#ðÏÀZ!ׇܥD[«¿¿Ð½dðYàfD”ro.Gziþ‚êÝ•"!UßNΪ>¢~Ÿ^ürO%šò÷1 î\ÃàcHƒ¶cy÷kð^4ÈônX,€îu#™í;¹+òq§¼•\)•5l‹ø.ÜALøÜvSDˆëP>Z+L­H¥\®»l*o½~:™dσ¼€‡…âÜ„…z˜@Ü#JÛøe«®r'ï[ã¥- rãYQdÒâ"óþkáh¯ÃK†Æ¸œ[~ýK\ÓbÖõ¢»_C©H>ÉKÿu•©1íU¤é¡¼+I/ðÄbZî…ߌÌÄúbÑ*Z€7T‘îà¹äÛëRâ8†ˆ¢°ø~b‰Ÿ2€rªa1ð~àFĽ`(Y | ¹¦ŸEzN†‚.D”—î þ¾°p¿½ójT‡ñ÷ Äm©ïEDûü•}!byÿà+HCf(¨fxÿP‡ˆ#O ÷W-9 é%Zü9°½Æù[F(V¸×‹8]«PhÇw›(ŒôB¡°TäŒó*d”»n@RQ6…ÖKä£Ã¯Á±b´·ÏãŒym¼ëMÓˆG©„ñ³ ? MpR²^èçžÅC&\R`™4ä“·.£p³>î’Wîd“?Þõzøûÿp8Þ±Hp¯ ¬ûù¦—ì‘u óH^Y÷¨à\mT™ZCºì+qÊ–àGwƒeÒ]ƒXŸwUQî`Y\\!þ› Ä주p“ßLý#D„M@Õòzà£Ô÷]t5âztCã:ÓGy×­Frç«€·".ck\¹ˆpNÿUd¿|ø;*»‡ ”vÄâþšÑù´kBÎíä»iÕ’(p+0qÁ98DåXFå|ç,5ÆÃ`ð²®ÙTC‹ÒÁ¢ý%¼M¬åÚ_—ƒ*û©•Ê?ÆÑÙ|t(_í„Öµ–cu.G+Ò76ÆûnžÍ„ñqR‰‚÷P Ô!_ßTî7P”?ƒªò…¸XÓ5íïÑ~ÃÁ -‘кïB£‚t¥’ÉKf)n]¥¥¤´ãŸÖ8y‹“[YtDg×Ç?6X‚<'o½09® ¯Hñ¼Âùˆn7T7ŽÐRc¨lír‘]I@ì~^E™Õˆê“e ¥ÝV:Ëÿ@„Q‚ò7_œÜ»aC'Úgðbädš¿ç"¢¶šß@ F¥ûìßÀЈö€YÀß#">Œ|ø"µí ˆ%ú†!Ê8|ø8C'ÚÃ\ŽüŽÇFð9í±÷a@œ îY =þݹM…Vä"2ûùcæÐz¸Œ"Vdc@+w¼a+–´és}—–{GÞë2ô%ÏâÒA9JçüÜ Î7üÝøV÷l}³ >5é”áuËa×a‡ûŸ†˜“;ŸR1é gV ׯŸ›‹É?6›¯êß;dh0¾Êž‡¢>–š2ÈXŽ.ª³Ž{ˆû㔟‰5‚XÝÊЄákD¬Ñ•XËÀ#y¤(/ÜüýmÀ'ñ7T¼€D~¹®Lš^DÜF¢mD~Ï>䯘ŒˆÓiTßÈX|øSj+†ª½¾K16±B~.âæÒè{{)r?‚œk×­ˆz%ñ 9ÿêpD›jiA¬Ò/!ƒ.GÍÀß ƒ^jüìFƤô Ïr]ÇRþyò›mÂZ|N{¬p¯É$FGDü*…ÖN^T™¢~îaÑWÌwºˆKFéÈ(ýdVp äìKz\qa×®êÀM{¾{GÈ&åZÉÖ¥´ˆwå¢ *šuÏ),_ˆ*óÖKᕃŠ-{ñþçž=ß‚khB' ³îBábJ¸Ïôû-rAÙƒÁÁ…3²¢š\Æ<«ãkÞLåaTéâyàQÊ‹Ia½X_e¾á"$¢L9º€Ÿ0ð •„eâ~ô6ÄMf0T+ðÒH”•kÉoN'küoÿýÀ!Dð$é_ÿ/{6b|Ò+R‰÷#á"Xe}«!IåÆÜdÄÒ¾ Äþ>äüÄç°¿},¹s\M.Ž}%Þ ýO¤·á‹”Ò ¼~ ñ«¢'1Ý/E VšË1ïfäÏ¡þ7âÞU-.r=ïAB­îAîã¢Áš‘hJ¯E&‘{ÅTihÖ3´¥åÄ ÷zâ¿jÄ墿_¹¿Á—¥aËp¾P,&γ‚1ä…ž‹oÄÒݯ(ÕOàö%]Îjåí×O!¢錗«C1¡®ÈùŸ‡lnx¢©@¤‡Ý`ŸÛC‘räú¨lViWÑÒ7¯R|í¿4=IñwÏ»fAzÿøÂk×½°N½e"Ø„¿ço–HB&¤k'`ª3‘(•ØHõ.IÄêþ:Ê»ªL®`h„ûeTvaØ <4ˆ¼3”ov;ˆÕúC”à‘(<ë‘ØáI?ßvÄ2^­ËH¼í~žÿ…éøÖÕBÜœö#½w#ÂøŠ ÇÅñþÒ0¨Ê ÔàN$ìe!i°ü‡¾Ô=û]$|à'!]‰Fdêï?A„b! þïÆëá"i‚²—ŒD>©&Л–T‘öTæíÈüÕ`ëý‘ó.w/oòÓ| Åù$¤d¡E¿Ëiîu#Ž Þ—Êà8N¿8îiSn›1~˜ðþÐ1yQdò ç-žç¾ß¹¿MkE*mho‹óî7MaRGŒt_&Ôp𠕨?øT“×Þï5]ä½m4Ç·¼‡-íás ¹È9„\WŒRþÌ« ”G2©X:Þ|‘æ‡ÿ#þÿŽ"/Nzî܃ÿó¯eéž r=-ðá(?¹|ƒ1ºNÄÁó{ <ãáXå~²hà*GÃèCÉ@¬}Dâ2—ãFàëä\jÁÄšZ©þn¤Û} ³X‡q ø%öïB¢ïü 7XÌ‚tEUÃ.¤2ñH&ؾ IDATþ'ç~dÆÄíˆeùfÊ7“ÏAÜž¾we†IQ>ÚÐ<äž-ü}{€BBVVjdD~ÿ'——RÙp1Ò˜)Ö“t‰Ý•ï©£ˆùH#àÏ©¯¼i =ÆÈµ/@¢ïT#ž»þ…Ò  bBæx ¹¶ÌаŒP¬p¯I0"Dµ?ÀÑ/_<‚„| X˜Ã‰| Ý;²~Ù*°i üh‹9K}n¿á-Wv°|q3©„KÎ $0‡ý×ýÊ„© Y÷p÷àðª’ö@øT©u9$å*.=vrx|“GC,×ó­cž•»xÏEá¾|W¦KL1×¥ÜáE÷9ÚÁhWœŒÁúÊœ47ï©"ÝFÄ‚>¶"⤒p_ \2ˆü˱‚Ê‘C5.3ÌB)Öpø9"HÖVÈc † 1ðÿ­{ׂ¹ˆ³-ˆEøÇÔf¼B¥A³Å,ÔG‘(,ÿRáØB^þ çq}…´íˆ Q!‡ëô­” 9q:i8Tjh^‚4’*Ý;§"1ÄÒ^MÈÌcÀ_"Q§;xú826`'ÒøªÁá–ˆîu$ø v´Æ‰8㔥ü²+…< §)6@³XxCô$ ׯjçÚKÇâe¼")Mþy+aaø°÷·)ß¿]7€v0ALJcÀ¸~Z1Š*W¢®dõrè]è…ªúÖpOi"ŽæÍçkŽðâ+Š–¥ŽÁ‰HD9Ôàe; DDëý(u .}y×™Báî_S椹±LŽ«.ƒXR3 ü‘P}åʇXÝk)¢×PÙgÿ!ªŸ\g  …i€ÿ‡X»k\žap¿O5ìB¬ø?¥¼ÛÓ Ä¾©Få¤w§±®þû Ë:Šø\Ϧò¸ˆbei´ †$Òp˜ƒôZ”c*2.d$ ÷+°•èþð•û]dpðW)îÞd9 ±Â½Ž?ÊŠˆÉ2ƒS ¬áEÅvÁ¾âô|«2…ÇøŸÉ¤aÁÌ8o¸|,G‘._!$ÒûÙŠ øÀbX»=ŒIC¦“8‚!&n¼„¬{iÀÅÆx.ö­Óžˆþ@ê*oþœF)’hÚšàª9Üý›&N$4-M'B,%LjÇâòo ÓFú]» êÉ6ò¯}±k¾Î…ÛGãyâ cŒ±Â}phD(ÿ-•géÎìÀÃ? þוfR½%{YN˜ Èl©åºc\ÄMb°ÛT YŒ!VÙZ‹öz ò¼ºLšéÈÌ¥µî.‹RóÄõèdØ…4dÿ•E:ù ð³“,ûby¿é½(…ƒÜÛ È=8RhF& «4æÄ ûû5.ÿçÈýùåçk¡Xá^gŒ18‡ˆ Í^šo9û¬çìØÅEwq—BÇ—™ZC*cÚ4o]3qcâ¤.yÖóìá`çQ ØMZ¸Ic¼Æë7ñúÀK`L÷È>¼#{}c¹KÖ0¥²sÂb?+šÐá“Úw©ñ­øAµ ôuiΜò*o=<óóÇpœP½ý뢵&‰à8±XŒææfš›šhnn¢¹¹…ÖÖ¢Ñ(±XŒx<ê­ñÆóü߯¼>|͵Öx‘‚ –0 é’¿ƒêbOw#~»ƒžFDÿ5”Có³©p_€„ ,ÇzDˆ—uá?„…ì:‰2‡“È€×rÂ]#.‘O6#÷h-ÊüOäïãœ*ÓoGÜ0jQöïßìJQˆæ"½ICÕÃ2œ‹ ­Ä}Àÿ¡ö>üby b$°œæXá^OŒ¸eh¥ˆDÒ‰&SiÒ¥ÿöD9áVw%H¬ñjCižª¼€ôP”‹[¾±¯Käýµ›½õ0"«î?Eâ«×‚4Ò蹑ò‘ˆ&"½S#E¸+äœ*M²Ô‡ŒO¨6 Ò@9|yþUнoåXá^'€ãûRƒSµ ¢½äÒåù¨‡|¹Uh)ßöÂÏ¢þÙ!7õ¾\öÚf®¾¸ 7í[¯M¯=ün×`\ŒÛ ™pbÜ^p»1^JºñKdõ«¦Ÿ!Ç(ü^TDD¸/ÜsñÛ ÂOªp¯„ç÷ÄBo 0n˜¬å=M&­ˆE ñæcì>4†'¶â½Äœ©Ê]±ÞÞ^zzz0æUœH„X4J<§µµ•¶¶6ÆËøñãioo§¹© ”4ÀLv0l~ŠXÜ# ž18§nçlÄ·y±'7©K˜bIŸŠø/Cb/CÂ>„ï#–Ì“l¸q'('ÜAüÒïâäDØ8à ÒA„ûPLúTŒß#bp¤„ŠœS&Í,¤ÑT/áþ2ƒ÷k/†‡¸§rŒõƒˆ·–f ±X í8x¾KMð»y¾{Íi6ÓùÀ7At'–ÄJÕ‡´òÑÞ†ˆŽvÄÊUMŒè0qoù$µ Ñè!×|™n¼ ‘nì“îg!Qjʱ¡‰_ŒðkF®‹L˜*ûçO roG-y€Ú¸W…Ù†X³+ ÷ç¨}/ÊAÄ‚_N¸kdvÛ‘Â%ÈdYåH nJåB€Ö‚cH¬w+ÜOs¬p´£‰D£/=7©P¾è rùv}É“üYט’~ï¾͸†¶VÍ_×θö™¾HNBº“9™Ãx™CàöŠˆÏºÅ˜‰C‘`ªÅü<ƒé¢Ñø¹ã¯÷Ï;OŸg ,õ€—ï½½çÎ=Á×EøÄ÷â¤]ˆ Pö3ۖɸd2½ôöörøða6mÞL,câĉLž<‰)“'3~ÂÚZ[¥‡EëþÑkN49Q>”ô „¿F^rµb"t./“F#!Àà}\/£üྠbý®×ÑNFþ„9ÝTÙHe—ˆZ4kíOß…÷JaLŸ§öD{‘†C%&!Úc$Äs¿”ʳÓv÷ס. qû+¹|YF9V¸×‹GÜ("Ú!‰àùÂ=4l²âàST1Wš°Ð/-ܳŸZsÅÊfæÍhÄM$Å’ž>„I€ÌqŒ×+á³29,Ö–6ÈÊHv¾…=;骂à9®‚2*óˆe?4pÕ„ÜlÂû² å`€dÂð¦×æ‘“ùå:Es…Gp5¢:lA/ÜžL$èììd×ÎÄ7v,Ó¦OcÚÔ©L:•ˆ#rÆór½–Z°qùµw#éBÂ=®¢ü3ób`1ƒ ÓØŽøÉ—ã"öêÅ‹Ô.AæØfÜäp»ÁëCŒ?þ`Ò¡Âx!á^ä0*ö±˜˜G|ܳá!}z…?à_ÌCÚshˆ¥ùë7â•ÃXû2´5T¡˜h/e!/p˜/ìÕÀqȤÓìß¿Ÿýû÷óBôñ‹;–î®.´Ö§ÓàÔ¡ä8ªð_º¸æ !ÙÞƒóRŒnA&(R9÷#ˆå¿¸H8Ì¡îþ ¹ÖS¥ÙH¸¼iþöfrâÝ -S«È·ÜÀÊZ²q«5idD%jÙ#f?Ùð`%i«°ÿTa†¿Tâ·C]‘‘߮ҽlÅXá^7 Ž#"ÚÓºLt˜Ð!a_2}‘¯ýüµvȸivï=ĘØAV/îÆé:B*‘D~„g1BÑŽXÇî*h#„?)¾-õÜ">ìRàÂí.}Iͤ¶>wCïùV G{ 1jrn7Ù(;Åcµ›2פðØB‘¯ù \Ïåð‘Ã9z×ue†\oh¯õ(Æ V§û‰Ncð1Í«eÒ^N¸+Äj>‰G—¸šòþÕ.ð+êg¥L"ƒ‰Ob1_ŒŒ—X ž‚ˆôFê'¸kEŠ¡ ;éRÝßÂPùë@å„yc…ý§ ã‘Þr$€gëP——¡iðYFV¸×tªãDˆF£"àBñÐó––p‡ÉV ¶I—õ§ö<úúúØ»o7[¶î$Õû*ÿô'šÉ­è ,Éu~~šœ«L¾› ¡Jÿmý,òÙï¿m㇊ ¬ö8(åÐÝå¼ùG¸óÚ8þ³"Aç„)bm/¡©‹¥ËÛV$¯ Á¥)Ðu½ò!n,¥8Œ¸ÄÜDVYËÉ…š(?F¬îåÜ)– ƒÇþcùŽõ)'¦62ôQ+¸œ\ÈÉZCë^‹Œ/x •d4 É=ø‡&ïá"÷rN‰16IÔp1i–£“Ú0.‡¡¾Ï<Ë)ˆîuÆ@Öâ®´&/hc%ážu“Qy"W„nnR!×u9zô(;vì`÷îÝìÙ÷*==)þþcí\x–"8EHšÐgÈÂLTšg‘/vlÈBo‚è6ÊŸ‡‹Q©d”[Î;ÈK{&óíGZÃó|òŠÌ³¾÷æý¶»¶á¼CÁ H¸½rS»A¬î¿ úÙI—"Ó”ã—Ô/L!ÈïUÏòÂDËú›×£)ÃTËÀ ‚§lšáF!=g• }o_!§ÀËÛ2œXá^GŒñ0ÆC;ÑX'kq(1ð4›]å¥Fi‘þN$B:bÇÎlÛ¶={öÐu¢ c ½ ޾™w^¯ðÒæÔôÐ0ŸøyC¾'äÚ-Û‚°™&ˆó—´çÐ÷øä5GXßÙÁ3» ÍñPxI“›Õ4ãÆ˜²OÉÜïv•ñ×C ,0ÙüÀȽpÊ¿»jƯBü‘'!>®ˆrpÁ2Hä.àbí݇¸Ä¼Ê©1Ez/5æõ”~v*dë4ª›dÆA„~¹ˆ;ˆ›L­\+ªiPx Ï5_ |i†òO›?Ê!b´ˆJ Z‰½ÔoN‹°Â½þˆhM4ÅU:$ÆC¾í û¨(ñ:ç;â8­4½}}ìØ¶Í›·°{÷n‰ÆGÓݯYå3ïj ª É4#çÑZÔ&Ï]†œŸ|v£JLå€JùŸ.½ ×|œÏ\ßÀßmåX/Ä"AY*ßÕÅø£‚ –÷=”¾X/F~”þÐy^òà(âÖ²i†M±°5+ˆ5g4¤ÓÏY+¼^tÛÒ¥K×nÚ´iƒçyËËd½páÂ…«7oÞüDèЧ o»ë®»&}ò“Ÿ\ÓÕU:Túœ9s6ýêW¿š´lÙ²Õä®›*X7oÃûÃ×YΧ>õ©e_ùÊW¢©Té÷X,ûþ÷¿ÃM7ÝöÕ¯æÉaЬ—Úf\×ÅquðàAõ¾÷½ï‚{î¹ç&×uOÚ®dÂãÿMJ'ž1:ô›÷Ckí\~ùåSxàYHCÒ -nè3X/v^L:5²ÿ~e*¸Ã544¨¾¾>¥²“SÔ„ÓëÉ2t8TaèC3VÁb)‰îuÆ“…3)X*Ìcž•]d`$A;½½½lÙ¾• 6²{÷n2™L6¯ˆVô%=ƶhþêýÌœÉFŽh/Fø•äÛi 7åN0làæôôŹxÁ!þäÊñ«8®§ˆè.Òë1Ð}ò½Ô—Ó†°{L „*⋬°ø,©¥>ƒu‡\8ÀÂÏðRl[Ñå©§žŠ¾á oè|ðÁË ÷ȸqãÞìØ±±íííAC¤Ø¯=º0•J•Œ¹­”ò®»îº½Ë–-[@mD™3f̘±*~ªx¹ª©©©±&™tGïÚµ+~ûí·_ó裮r]w@¾ÏÑh4ÙÐÐÐÛØØØÝÖÖÖÝÒÒÒÝÚÚÚÓÒÒÒ×ÐЊÅb™††† Àý÷ß¿rÿþý³Jå‹Å¢W_}õJľÒ9Šøl£óŸÿùŸÇ¿ÿýïo;zôhɃÛÚÚZ¾ð…/,ÆÌh—]2¡Ï ¹F­)Xï÷ùÅ/~1ò™Ï|Fgߥ±b³<šÊñÛAqÛð`–ºb…{1þ?ÇqˆÄb(­ ÞéY?u¥roy¥òÜ-´Ö8Ž&™L²mûv6lØÈ¶íÛH&“8ÚɦSÒð<ÃßÒÄê©^3²E{@¾gJŽ`W#穌";@5χÞÁ3ŠÞ>‡[Ï;Äó“øéºÍŽÉ^ó~e!¿ªz_9yåöGa¬¢hhhˆõõõµ“ßa1¬ >£þêH‘õbß Å¹ö?s-¶|ªÝ–·½¹¹Ù¹ñÆ·=ú裉t:]òžqãÆ%O=õÔØ+¯¼òp©4étÚ<òÈ# ’ÉdÉpmmmG®¾úê]ÈyÔB ¤¿Ç0°ôEïÚµ+zÛm·]÷Øc]ZíA Ý'NÜ?}úô=Ë–-ë\ºté¡E‹;묳NtttdÈÍÞ ïì³Ïž[N¸¤¯DpϦ×ÍÍÍãÇ)ûnmiii>ãŒ3ÎfSüú÷mÐ Ä|XÜg ¾{ï~÷»Í÷¾÷½/¼ðBÙÊŸy晓֯_?³ ÏÂÆHvŸRªÚ{o´ˆXEuúhÔ<È-#+Üëˆ1bqDbÑÕßÍ¢¸Õ]FH&Sl|é%ž~úöìÙK2™@k-dBægÏ@WŸÇ‡ojå7Eq3§ŽL¬*˜Ê`ayfBÂXhr“4‰qÌ%Jc$Íç®;ÄÁ®É<¸IÑêOi10kz¾?(¿Ô€Vå߯;u~“BŒ1‰üˆèðzð=Ä.¸à‚ùk×®-ë°xñâ™ÀÈ·œ‡{x)Õ4+çvQì{à7_k¼[o½µóË_þòÎ;v” yâĉñßùÎwμòÊ+.‘D½ð ­Ï>ûìÒr…-\¸pëš5k3zQ€êîîV7ÝtÓëÖ­»¨šZ[[¬Zµê‰n¸aãe—]öê¼yóúûÐ,…¿»J&“*NU­¼ûÎTò‘ñyžÉåþйqèx<®§L™2±’p¿è¢‹–#3 ‡{ Ý‚²ßÉNŸBzaŠ.7ÜpÃÔ_ÿú×Ý„8õ¯¡:ßõ(£Ã$fAXá^Oü׊ÖÑHÔ\šß3¬u¾hŒD"¸—Ý»wóä“kÙ¼e }½}(­ÅÊ䢧ÏcÙ¼¹%BÔä0RJÎÉÑ sˆ7óÍE\ÿšhÝ*2®2Ü{ÑCD¼èD:ÂÄÖn>yõ Öïiçx|ös—óù½ç…è,Q±‚ã_÷ eJzO Œw’Â%xᇗ8ò²‰Ù^ŠùIæ™={ö´§žzªì‹:7 ñˉî@ÄœêxãÇ×+W®|±³³s¾ëº%Ÿ¡k×®=óàÁƒutt÷Ýwß´ƒN+u|$I]qÅ/2:…ú«¿ú«åO?ýôÊJ #‘Húì³Ï~êÎ;ï|ø¦›nÚG¾Ïþ©>ûf-(×@ ãc´çU5³[`Qvè•û^ØH »ê8W\qÅÌûî»/šN—Ö¼Ó¦Më¸ûî»W­X±â(Ò& Éжà™Ð¯\ÇqT§:Ø#ê¢Å´02B[ZFV¸ŽÖDcQ_C ô}Ý#‘Z+öîÝǺuëxö¹çéêêB)…ã”6¥2í­š¿|SÇ;$û†Ø°d¯B"Ýå(ðg;íIŽÍй§—›LÓ0sŒm„¦¸îyñÏóç&ʺþP°2Â3ÓêÐc< ZÓÝ×ÀYÓŽñÑ×5ñ·÷Å1€õ„d‹Î³Â–Q _ÎZo Iç½u|^Ì$ü=<¥{Œþ<^°­œEh°‚0ô«W‘XT}á`¾‘Œwûí·o¸ÿþû/9räȤR‰^yå•?úÑf~ìc{™"ç~ÿý÷ŸYn°ä„ öèCÚÄ(´¶ÿö·¿÷ƒüà ÏóÊZÁzn¹å–_ßu×]ÇãA¨Qw=†ƒB!æò“A"\Ub"òŒ=g¶ŒR¬p¯ b ž÷hT&úS*'Û ¾¨Féíëcà xøá‡yå•W€ÜàÕbÖM¥Àõ ©´áη5qÅù’}µ²ëæŠV€ÖàD5ÚQàÈ“„cÝ.{gؾ³Ûúxig’—¶w³igо>hk„¥Ó`ñ4‡ESaé4—y“aB+´6@Ä‘r\Ò!1œLUçã½ëÓ]…t‚qð ô¥o?ïU^Ø=•_>ïÐÚ@ÖW^®;ä½[ µVÐ0(Óe/æ Æ¦L™2öÀ¾= é/¶Ã¢»Pœ~ÛÙÚ|–ÚVŒÑ"¤ë¹òÊ+.Y²dóc=VR¸§Óé¦x`‘/Üèõë×7mÚ´iQ¹BÎ?ÿüçg̘‘bôýNæë_ÿú²ýû÷Ï,—(‰¤n½õÖ_ûÛß~,8®u³ C•nBáô”ÿM#ÈŒ¹P¤`Á‚s* ÷3fLúüç?2'AÒXöƒAšÜxLø»RêHç1y>wW‘¶VX ÿiŽîuÅ€ñp"¢‘¨¸]hñ:PÈŒ§(غe+¿{ðA¶lÝJ2‘$qrÇ—Àó «×ã=×·ð‘[cdR^6ßZTZ)¢1 æ›×ÓGNxtîKòòîÏoîá™=ìÚ—áб ‡Žy44gÆôÙ,9c!7¾ã¦M›Æ¶mÛøÃžá¡í;øáÓ¯é;Âäv‡q-Š…“aùlÍkfxÌè2­Ý£­Qô¶ñ ™´ëP:§¯œ@ò›äÖq㟻î{NtðÔ%e^‡°ÛKx,jIËz."P0y–ça* æÏ¬ U%ü"+õ9ÜIF;Ð7ÝtÓ3ëÖ­;7•J5MdŒZ·nݲ§žzê±óÎ;¯‹œ¥Xýë_?ãØ±cãKÐÒÒrüï|gyG呉Z¿~}óo~ó›K*¤3çž{îSV´Ÿ¶T2>¨j\‚ZZZšZ[[çѶѼÁ·ôìkÞûÞ÷NûÖ·¾åùc~J1Ò¤IS8PrzÑT¦Ò2Š±Â½^$ÀDLvpj4ÍF# f<=~ì8>ø O<ù$ÇŽóÝbtQ {!]½KçÄøÄí 48Šd‚ú“uw‘:¥AÐ6о—]ûRlîLñâ¶^ž{©‡-;ì>àâgÌØ©,9ã Ö,=ƒåË^Â…ó™=kMMM466úâÕ¥··®®.^~y[¶nå…õxqÃFžÜ¼…‡¶Æd’4:)fUÌŸª9k¶fé4܉Ú24Ǥ¾®Q£ðüÅå«_'8%Ÿ!І§ô¥£LjKñÙ«»ù㟵²÷¸¡)\šü¨ŸzaH‹{ñþã[Üý‰°‚xÑ#‘ª,lALmF—øro¹å–_ýêWwlß¾ýŒR‰Ž;6áG?úѼóÎ;ïyrçï­[·n^&“‰–:nÑ¢E›.»ì²W©ý½QµUÔ³…ÕöwSßùÎwœ8q¢d£`̘1‡?üá?ê­É5¨&^ºëº'{ÎFk]Íß¡rjy}‡óo2p‰+‹çy'{o@U;Øu]—ÒaMÃ.ˆa"Ë—/×Ñh4YªaîÓzÝu×½ç›ßüæ#È`ß$bÍO >òÉж td8´§€ûÈX¡á˜˜Ìr a…{ þRµ–™S '*‚rËæ-Üsï}lܸÏóÐþ ÕJO&$S†±mŸ{O 3&i’=¦¬h÷ ¾âîâÈÀQE|_\—®>xõhŠ=¯$Øðr‚ /w³öù.¶ìréM(š[ÇÐÑ1™Ž‰“yÓê3Y~ÖkXqöÙÌš5‹±cÇÇK–¯µCKK ---L™2…‹.º€ÞÞ^Ž9¶íÛyöÙçxþ… lÚ´‰ßï}•_®?Dª·‹ö¸Ë’i^;ßaÑÅâiÓÆ)Æ5§iŠËùày¸žGÊázžQ~ïFØp’oDQÊГŠpÞìc|âŠ(úó\£È;¤P˜—šx©Øvå[é• ¿­Gº ­Jú†±‘|žE™4iRzõêÕÏ–î©Tªá÷¿ÿýbàü?ÿçž{®içÎ%ÝDÇÉ\zé¥/¶·¥ã$ IDAT·1¼kÉ@ò’ûóÉ'Ÿœïy^Ùîþyóæm½í¶ÛöRß&UA;Ü £Á¸«Ô¬ìêÆÅæ•=ØòÛ˜+,¯T>fÖ¬YÇz*wÖ¯_¿ xÕ…y†Ï3pÑ D|qßÉûN~O@6òoΕc9M±Â½žø¾Žvˆøî2ÝÝÝ<þÄÜÿß÷³oÿ>”ªtÕ3Ï2®áConáÚ‹#¤ËŒƒ7Fôc4"¾éDxŠDÒãða—½ûzÙ¹'Ŧí=¼´£·ö²}w†¤i¢£c3¦-áš7Ìeñ¢…,\¸€3Ï\ʼysin®MÏ]SSMMMLŸ>Õ«Vpôè¶mÝÎK›6±eÛ66oÞJgç.¾ÿÜ~Žüî­‘.N‰°xJ„ÅÓOÕLç1uœaL£¢1.ÁPÒCÚ5¸žƒA¡‚–K1ÈÄXsæq~·)Æ=ëZâPhqÏ}WßÛ?Mà¥òäú¨Ò±§%7ß|óæ_þò—Ê Rݵk×Ìgžy¦yÅŠÝ€zâ‰'&”s“éèèxeÍš5;‡¢¾mH Geß¾}Ë¥ÑZ»Ë—/ßA E¥ëº¤R)û¾óÎ{ ÕþæÕŒò,tC,u|¶WìÜsÏ=ÖÖÖväĉÊ•ñòË/ÏݰaC|éÒ¥=ôoŒ„¿GÍÕR°? ·Y87¤Œ1ÉéÓ§_¶gÏžj^¸öå2б²º‘À˜žç‹Einjfë¶-üè‡?aý‹/’N§qœ¹çLuœèñxÇu-ü¯·7àºà¹&;€S)ÐŽ%ÅÑà€IuÙ²'Ás›ºÙ¸µ‡M;’ìÚçrôD†=M­,Y²‚K¯YÎ/fñâ…Ìœ9“ñãÇ1fLæ©)cÇŽãÜóÆqîyçJ¥8qâdçÎ]lÞ²M›7óÜó/ò«û·â%ÑÚmšYã=Lv8s¦fét˜>Î0¦Iu<2ž"ãŠX7PJ“r¡!êñ—×u±÷x;ÏîV´Ä+G)½.ß1yñƒŒ1wH,nõ¤Úºôó,…yýë_påÊ•OÝ{ï½×—JtøðáÉ÷Þ{ïô+Vl܇zhN2™l*•þ‚ .xöŠ+®84$5€EvÜ)ôÚµkÛ»ººÚÊ&ÒÚ]ºtéj‰èرc‘d2Yq&ÌXÁ«ã>d÷ª ]ÙU¹¯Pþ™0û³r¥ŠŸk±–KÞþÉ“'«¹sçîØ³gÏ‚rÑŸŽ=:åK_úÒYßþö·å[¬±P¸^¸ÍŸl„FɳÜïÙ³' ¼‘ ƒSµÖjñâÅã7lØ0 ÿüH<Õ^3Ë©îuEþNµ£YûÔZ~òãŸðòŽ~Å`ÕêP ºº ç.iàsïn"7¥ˆ7*p4x‰”áx·Ç«ÓlÞÕǺ]<ób_Ns¢×E;ÍŒ0…¥+–pÁÊóX¾|óçÍ££c---Ùè7§ ±XŒ &0a–,YÂ5׈˜ïêêbÿWÙºu/¼°žuO?ÃKÛwðÔÓG0k{Q&ÍøÆ g΀e³¢,›¡™=Q1±MÓ7D# Qd ¤Ý(“Û]>{Mwü¨‰ã}Ð…<«z¿©ý·çV¬«`0rÞ3{$ Zãy^U"…‘}žåpÖ¬Y³ñÁ|]"‘(:ªçy‘‡~xÁ§>õ©{öì‰oÚ´i%,~ñx¼çª«®Ú‚¼À‡"FyÕ¿A ü½û•}àÀ†T*UÚΧ­­­R¨À ¶mÛÖT©Á⤄{ÝEJåY‘¡òq¯qT™RÇî50Ÿ…ëŶy—\rÉKO<ñĪr³$»®{ôÑGW;vì‰ööö BQοÜþ°ÿ{^Ãõÿñ—¾úê« JÕ#@k­gÏž=˜ƒÊ—>9†ió#dN¸KzÜfçž$/mëá¹Íݼ¸µ—Í»\Ò¦ñãÆÑÞ>Ÿ×œ;%‹qæ™g²dÉb-ZD{û˜ŸiýˆÅbŒ?žñãdzôŒ%¼ñ1~;f×®]³K¥ëèèØó–·¼e7¹n­©‡¸*™_OOãºnÅL‰D`ɨEÙæ¡‡šrüøñ².ä‹¥A ÷jŽ5>'YÖ Ë‚F àÞ¢s/ZT¨ŒB¥_ªì`»û¶·½mû¿þë¿8tèЬr…ìÞ½{Ñ]wÝ5÷ÓŸþô6òÿvKYÚ ëWl §ñî¾ûî+Òét+Uà‹î`6ê ¯`ò½Àš¾.91ŸÒƘ~‘vBÇYa?ŒXá^G\Ï •bݺ§ÑZûV~ï{žAo\å°kï þó›w$Ù¾»—{ú8|ššÛyÍk–³ìÂ…Üô®E,Y¼ˆyóæ2aÂÆWvðèh¡££ƒŽŽ.ºP¿öõöräèQ<Èö—_fëÖí2vË6~z÷V¼¾ŒoSÌcÁ´(ó'iMëå‚ÙšÿÙæ`tÖŠLógT Ï• )]äòzfH^žõ¤êéÝÙçY3sæÌÔE]ôÌž={JΤºoß¾™÷ÜsÏ”7N8qâDG±4Žã¸«V­úÃøñã]×uã8C)Ü+æ=VYÇ]­uYS©çyzÇŽ%ádËVû÷ïw~ðƒ¬r]·ÚîÓî!Q^–¡´]eÑÙäµ,»Ú¼ î«“)¿ò´©â’Žä’·»Ø!á/‹/îYµjÕã?ÿùÏË ÷d2Ù|×]w½aõêÕw­\¹òÅ-êÅÜhÂi¼ßÕí·ß~aggçåê=ct*•rBÇ÷‹dS°ûƒ;ÁÌÚáýnèÓ\ÿºšÐ¾lyVÔ-V¸×#"å¬ìƒ7ê$z»øéáß~áqäDÐ,\¼”w}àZÎ;÷\,˜ÇĉŒ;–ÆÆ²ƒâO›š˜ÖÔÄ´iÓ8묳èîîæØ±c:|„-[¶±ö©u<úûÇøÑ[ˆ9†ñ-Ç9†ë6¡bMî/¯{h[¿é^Ãn4¹ ˜B¿ýH´é«ÉçY ï¶ÛnÛpï½÷:~üøäb 2™LÃÃ?<§³³³ä Öæææ#7ß|óÀ"ѳ”Õ\¸O˜0¡/–eÒó¿ª3Í?Rù>_ )vLèØR|Z71G‚ucLÄ£ý¥–»Ÿ6X‹{=1¦&s™*ZÛÆÐÜâây®ëâf\v¼¼ƒíÛ¶áyžçÑØØÄøñã˜0aÂÿß޹ǹQ\ùþT·¤Ñ¼5¶gŒß0b &ĹöšÇ5á¹`‚!<Øò!Þûá³ Öönn6$$7χÜÝ% ×$!„…½fI ÄNl60ÄÆ&ÄãÏÃöxÞ3šI­®û‡ÔšR©^Ý#ÍŸ¯?²º«Nªni¤_®†ùóçÃâÅ‹aÑ¢E°páB˜5kÔÕÕA]]ÔÔ˜^³5µp]úúú ¯¯Ï[†ššš ¹¹ššš ½½º»»¡··R)B!ˆemÙPQQ!ÛË e[`YÌŸŠ÷lJŒø.µ’¼öÌwQ™`è 7ÜðÎûï¿ÿ_d) d-ËJ­^½z÷dº¼KÒ9sæ¤fÍšu´µµUz=@GGǼ‡~ø³7n|Se§À~ñÅëxà›zzzf ŽR;›b0¥ñ1)+z×EhO˜í1Ã\<Ï?Ëú/؇ÃäÞ{ïýÏ={öœÓÝÝ=OÓ_ä•W^¹þÊ+¯´^~ùå_Ù™Ÿ×½üpS± ?üáç?úè£wvtt|F}”‚QOŒDÇ*+çÇÈ—‹ì„Câž½ 9ñÞ¯„J)%‘׃Â}¼ ð–,\Á$êµ, X„æüRJfo@”J¥ ··:;»àƒöB:‰Î‡ÃaˆÅbÐÐÐõõõÐÐÐ .„ÓN; æÍ›sæÌ††¨©©H„¿¡Üäcddúúú ££Ž9ÍÍÍðñÇCkk+tvvBWWtuuÁÀÀ¤ˆeA$P( ¡P*+«À¶C`Û6+9'„€g¿M€ð¯‡ziHÈFä@f}~&~H]ÊF/¦*&y¦l”çËÝwß}ðŸþéŸö666žë·íܹsï¸ãŽ?CéÏ“÷í¨4¢”Bö–òEÏ%—\²{×®]A·’O¥RÑM›6]þÌ3Ï||ûí··ù˜Gyä´'žxâÆîîî٦㢔Bæ&›c;ælš€Î†2¹×Åúû'žo­aFûµ%Y¡¬|sQJ½Õ¨LóÎ¥~´ʈA6Z,t¥(£@®»îºc[¶lÙô/ÿò/N§•_Š©Tªâ׿þõW–.]:ïÞ{ïÝüÕ¯~µ‰ñÅŠ[>§ìÛ·/úÀüÅ–-[næïyàN›6m¥4ÔÓÓs–ð`(µ‰D„9f^¸»’:Ó_úDÇ¢³=?n토pGLb¬Þ*¥£9ÔüÇ …ŒP'„€KÓy«—X–Ô"`“PæT’‰œ€Ì]DÝ4¤Ó. $ ñàAøÓG7íB(‚šš¨¬¬„êêj¨¯¯‡E‹Á‚ `Þ¼y0wî\X°`Ìž=*++¥‘äRB)…¾¾>hoo‡––hkkƒÖÖVhii¦¦&èéé¡¡!„ÁÁ8¸Ô…p8 ‘p"‘T×Ô‚m‡ d[`Ùöh‹·ð=Íœ[/ý4ïÕ’oNܾ^b>û#"eþ!ŸjkkÓ^xáÏÃÄx¬X±b×i§6 ¥ŸÜLô›Î½óÎ;÷?õÔS-º•:úûûëׯ_sww÷ ÷ÝwßÈ?§lä.Çï~÷»ºÇ{lù¶mÛ.òukx×uíáááÉó“Ç'Áuüë©}ŸfÓ}Lú2Y)HmgÚ‘üÇܵuëÖ 8p¾I·ûöí»ðßøÆ7n|ó‹_üâ»wß}÷ÇÓ¦Mó–x-Ëo~ó›ØO~ò“O¿ùæ›+=ºÔqœ‚‹ÓÂápÿ—¾ô¥ŸoÙ²å¿Ê„;ÇqØ éL#î~~ÐùäQFâ½h»¢=€Â}œ8Ö×çN¯ªÙû—ìì>[&+gE¾KÝÜ <Ÿm¡—jFs¬fs-ÁM§ÁI§¡`ººº ñàAx÷ÝwÁ²,°m;oýôyóæÁÂ… aÉ’%°páB˜;w.TUUAEE”••e¿t"NÃÈÈ C?´¶¶À¡CM°ÿ~hjj‚#GŽ@ww7ôôô€ã8™ÕY²©A¡p"ᄘQ_™Ë)÷VðaÏSî“&{žˆ r.\9F¶–»à¦LB;`ÎùÔ_ßÜϸ§òq³fÍš?¿üòˇUi1³gÏž¥ÝÝݳEÑüÊÊÊ®d2Y)[“›Rj …`lpãë>J¸¤Û¢ömºbQö— ^ôÈ¿RéÇä"`ï»AâK&8Eû4‹Ñ¿ÿû¿~ݺu3:;;êúœùÖ[o]»k×®Õ?þxãܹs›çÎÛ^WW×W^^îô÷÷GŽ92£¹¹yþÑ£GOÇã³Òé´tÉ·3Ï<ó•»ï¾ûÃÍ›7AfC)µ²«[ù‰Ž ]qu&Â\61ЂÑvsP¸—J© £Ë*…|ðÁ¹Oýä_-þC]˜#À¤\'óY äÄ9g›—.¤œT*ánƒ÷Þ{?Å'™´›†úz˜¿`œzê©0þ|X°`Ìš5+—G_UUÑhl{4}Ôqœœ8ïë냶¶6h?rZ†Ã‡Ccc#´µµAOO¤Ói „€mÛYnA(‚h4 –Ûʤ¸°~"ÄÿªjIÎ{î9÷·»g#8ï¢;ª²ûü„æ°L".™]¹re÷§>õ©ü÷yóæí¿êª«ŽÂøœ꺮ÑÅ©¥|ÝÖ­[÷‡;v|¦¹¹Y»ÚK<mß¾ýŠ7ß|ó¢Y³f5MŸ>ýx8Néïï¯êêêšÙÛÛ;Ëqi Cmmí‘[o½õÅçž{îjY¤ŸRjŒŒ„alÇk¼$è…SàþuF¼Ž»*²ëa´Ô£é ¸¾”Ñ_…{ã7Ù¿ÿ³ßûÞ÷¾Çu÷ð ©Tªº½½ýÜöööswîÜÉöaež9sæ;ßÿþ÷_¨©©I…B¡”¦?oª:Ç¢sbU× Ó îc$1*€JæQ}Ž@¢]tÑì}úË¡~RMtâ\VžMxô ´öl´?“ãmMlˆ„#å£ÖÍæÑ»4sQlwo/ttvÂÛo¿ Žã€mÛP[[ Ó¦MƒX,Ó§O‡¹sç¢E‹ ¶¶6³ŽúÁƒpôèÑÜÅ¡===088™*…ÂagóÏ-+“Úb[£é-yË- 4?½ˆ9N‘ –‰káPEÑxOÑ6?‰¢i`jç¸Ã… Ç ¢¢‚\}õÕïïØ±ãÂd2Y¥³/++¼ì²ËÞÎ^”:^ï£~JymÂç>÷¹þûî»ïßî¿ÿþ¹###Fws§¼¥¥åŒ–––3üôU]]Ýñ·û·Ï\sÍ5-/½ôÒj•m___Æø:¸!wfU™¢ç¸Þ͸¯-1ÕM1ºk3!„fSed}™DÛó¶~øá?E£Ñ=öØc·uvvú]í%ox¦†Ó¦MÛóØcýàâ‹/îknn…B¡•ßD"á½Yq­Êmå·ƒ ÌDìƒÄui wC²Ý€*¨Œ0¯€dÄy2ö0d–=Êû©R⺂4S!n’‚À}ÈÄ©¢Nå›BæR¶eØ(ó&x@]œtÜtŽë€ÖÖVpB¡LÎ}(‚T*Éd\×…H¤ "‘p6½¥l+U÷î4J ÄÊ<{¹á¼˜&^‰"ú-Óâc‹XÃۜש‰Æˆ{!îÚµk›~øÃ6·´´h£É ‡n»í¶`tå‰Rc-QTÖ#½nݺßzë­_|ñÅS©TIn6‹ÅÚÖ­[÷ìúõë?:~ü¸Fã*ûx<^žL&!‰”&)0ú:=êíÓvÜ#îܧ‚Feµ©4¿~D:¿Ÿ·}ÿý÷7ÖÔÔüóC=ôß:;;ù‡oêëëÿðÀsíµ×vAV„Û¶íê„{öÂV~âÁ¯gÏn«Îì!²IRDNXážéÍC¯€Œ@¯€ŒH¯‚Q‘žk–}&Ì>»L™HÀ Ôé'•(ªËv˜—b!0ã׆—ùã}8’Øf_Õž?xv¥;‚P8”‹dÓ‚Qúc/ %…J:Ñ&‚6y~$õ…íEb›(ûæ·ù2‘].7žïuœòÂÝq"ºÅu]ö¦6'îœ9s:mÛN9Ž#îµµµGï½÷Þw¡p¢_Jh*•"ª ¥ÔN¥R¥ÝèOúÓmëׯïÚ¸qã•---gÕa,k¿à‚ ~ÿä“O¾vÊ)§$™UWW+oþÔÝÝ]700`•——›.?ÉCÇÕëN§Ku~©ëºÚÕ’É$+B‹Ù·÷÷.%N[àÿ³ÎÄqmÀÀq›É\2î|¿'x׬YstÕªUÿçë_ÿúÞ-[¶\ÞÝݽh¬«£Bh,Ûá…þÛÆ[UUÅ®ÕÕÕiBˆ*Ç’Éd%äŸã BÝ$˜`"âu“#Ä''„pÏþ!U0/z…Ñ<ô0òE9d-ÐÞ^WœÍ¨æu]×Í^ðI)5^yE–JC)'6}¤Ö˜¶ãŧ.o;¯œiËm2v’(µ"åDT&·Þ§dÜŠ4™<[ɘ =[gY@˜÷@ r\ÇX,6P]]}Dö~v'TSSÓ 'HŽ{²gÏžStë=¯\¹ò·+W®ìƒq~ýkjj†c±X["‘¨ÌæÿæA)%‘Hd¨ªª*ãôº}ë[ßÚ{Ë-·ø‡ø‡ó·oß¾¢½½}‰ÉÝN=lÛNÎ;÷à .¸`×í·ß¾ûòË/ïšN§Á¶íÜø/^ܺoß¾vÑšÑétÚ¶m;ÕÑÑjhhPF4UTTT¤êêê:(¥¶mÛ€t:mÇb±ã•••Þù-jŽ{]]]oUUÕ1˲\>m‡RJ\×µb±XJã^QQ‘¨­­=â8N™è½•N§íººº®P(”Оˆ®­­¬®®î „8¢ÏÇqB±X¬;‰8 ?ϲþeÑãQ___ŸúÅ/~±í7ÞøÃÓO?}æo¼±¼µµõÓÉdÒ× #‘HßÌ™3ÿxþùç¿q×]w½ùå—÷‚$ÿ|úôéÇ¢ÑhŒNús㢔†ÊËË»{zzH]]/ÔUk·‹î¨ªŠ¸‹&8ªÉXA9æ·cL3ÃÉ3Óe/õrÒ½ucÕ{T*‡‚ááaXvþ²_oyýõŸÂî(ß·o_u(~§R)kþüùCŸùÌg”) Ÿ ¬ßþö·±ë®»îôööΗUWWùå/ù¿.»ì².ß 9räHx÷îÝ5©TÊ}qRJI8v?ÿùÏ÷ÔÔÔ<ÁرcGõ¦M›Nþýïÿ©Ã‡Ÿ2888-•JU$“É0¥Ô²,+‡G"‘ÈP]]]ǼyóšW¬XñÑu×]×|î¹çä-+˜GSSStïÞ½Õ"ážJ¥¬ÚÚÚÔòåË{ËËËÿMÆãqûwÞ©íïï‰þ.Ç!±X,uþùç÷WVVýüîÝ»·òÀU¡PˆòÇH)%étÎ9çœþ“O>Yuc :;;Ão¿ývLt~2ÇÞÐÐX¶lYÀërmãÖ[nýN"‘¨…ByK#ð_ÞS0±njk÷!šÙaëÖ>ùÑ#H½lb‘«×ü Šô›œ'B$ 8t茌ŒÐÏ/ÿÜÿÛ¼yó/a w0ÿ 9Q> ­¯|å+ñÜsÏýµ*å’K.yþµ×^ûw6<Θ¼n:¶d2 ]]]ßH¾ IDATá={öT{k¢‡™g‘0¦Œêlkœ­JÈS® ¤¬¬,Gâ”Æg°Þó7÷áÕ¶y¹ Ê«°)°clMû`õ¸Ð¿'€=#_]¤:o<º±hë¡ ^—ÛÎGÛ…mEÑýÑÆÎ‚”ã@:íqgΜ٠S_¸# ßúÖ·.9vìØ•Íi§öî]wÝÕùŸ1‚ŒSnR§ ²è¼JЋ¢Ñ²2“‡*_ݤÜįllü1úúCÑ>6&T¸gsÒ dÄ8+Ô+`4=£ùèÞ¬0(çº?T^p›´ñìLÚäê¦M›–,F{»]wu-Zçgz”/ÓH$v²r~IIÑÁŠoÈkÃogl<ÿâHw¾,æÆî-)éC´ëÆ*·Ø'ÊãÖMØ¢4oƒR†tæ&UéSN9¥|è!“òíoû´wÞyç2PLä+**ޝ]»öwY›©œ"… ãû[ŒÏI™@—í›Ö‰¢î®¢Þh—Ù˜´UM4t~Eöº2Ós‡ŒqîÌ Œ,ÈêåYŽÑ†üµÑ½¼tï ^¤›þ!SÆ—Nسõ²m‘Ôçê¦M›–¨‰Å:ZÛÚ2weš°B‚\ f „¶²Üqv_æË$ï\–#¿Ù=娏²s}«¨Lzî8±.›a¤_QÇ¿†Þ±º® CÃÃ@ RV6pÞyçoŸÈ®]»*žx≓ÉdµÊð¬³Îúýƒ>¸ðuGX!éW¼«Ú˜èDv¼°•‰wQ™nÛoA¨ÈF'øuÈÆ+ª×ù@ŠDÉ„;s3#o—(dº÷ðr@EQs—«ãß(&k)²b_4kW n••­Ì=餓óæÌ>üá‡û€R\š†Ê5Uå•Ëöu¹íÒ‹E}ø±aíø¨sª¸ùÇ-ðiy'|Y¡O3?ùÇ!³®?’wŒ…ýyå–E •r >4”ºP«m9ãŒ3ø¼GdêA€~ík_»öرcŸRVVVùÆ7¾ñäßûANtDß~Ŷʯ¬IªHµ¬¡.²S‰kQ=êLÐEÜUþdçC¤Ó„`šÌØ)špÏ^@‚ÑtvùÅä u ™//ïF l¸ÓÛv™}ÖŽpul¹·ÏGØùp*uEáUÑv‘8gíEXçw^ãÖ­[Gœt:šN¥!l3©ú4+ø²­GÓ[x¡H Ûä "u¶ ›Ò’ëƒ0íˆÄ·ÄF%ºórãÎGt\D0¯^7V"ð-*3éŒáÛ*ÒiDýBÀ"  @b8Ô¥pÊÉ'ÿ©¾¾>(ܧ:äú믿ä½÷Þ»RuA*!ĹôÒK_Z³fÍ1Àh;‚ˆËg¡Jœû›DÜE¾uÂ\¶íí‹–TT=D9î )gëÛ× u“s(š˜ %"°pg„º·ÚKd³Í<³B˜}¶œ½‹À¨ð&\™(ê-ÕÀõ§²+8,I¾½*B ëÇ=ÿüóTTTvôõõÍwÝ´ðFLºˆ°jÛÛ7i«ó!²QGáÙù‘¿~MóÛƒ´5õÃþ™ú˯ïï×u! %Ï>ûìýÙ¥ñCn sÇw,ÿÿø[t7[š3gÎ7lØð&àÅÈ"¢T~¢í²r•Xgëe‘l•pæŸM¢¶ª¼zѸDÇ¢«3ó<0Ú^$Œ„;s©—úâ vÙZéÞ›’@~ô;Ï-ŒŠw>ÒͶ÷lMÄ·*z.òÁNt‚Ýø÷ÈëëÒK/íœ^?£±·¯o¾ëRH§Ó`1ë¹—:O]—³.òß ê3Åšþt}™¦Æ¶)N*LýèÒsdǧšœ3°lF†‡¡¯¿\J¡®¶¶é¢‹.jÿ~Èä€ý«¿ú«Ï?ÿüóH$b*ãP(4tà 7ü{öæ@øz#È(üwt1|ÅÎO´]U&Ì¢6"1=ñ®³5™ˆ¨ÎƒÝD¨¯/’E(Ü¡N`4ÍÅìl]® ˆE0påÀ•c+zQYÁm‚îÍ!«WµS vS1ŸëçœOŸó‡æ¦¦Ï§tÔqGÂL »‚ ÍÌ£]æ§@~[’oS'†<›üôÆ–‡8Í…µåÛ@ _O˜ ß–°õÜ8%mXÙ•jDç‰ú󎋎ˆî˯¥ïÙ Î9°ÞÞ^ æÏ›÷ÁòåË»s§"¸×\sÍ^}õÕ›‰DʘBO=õÔmßùÎwvg‹ðË A )Öß…ÎN`ÊÊdúEöìm«Ä¾N4«=¿/Š‚û¯ãdmùñªöóÊewÔEü‘îÙÔ/EÅ»+©·ï=Lfª4ÛŽÍC'PxA)û&aë)³ÏþìC8{")Í6ûÌ׳ý°vü>‹J¸óuîßüÍ×wncÛÁÎãg¦R)ˆ8‡GsÝ…Ñ\FPò6º\nv¿ _m* aNª.Ò'ŠýEÉ•ã{áD@_¯Í‹LPôçÉ‚D" GŽ´“†²²HüÒKW¿UWWçæ:O5ȶmÛªî»ï¾5»wïþK×uùûIÐÐаçG?úÑO³·eÇ×AJK©„;¿¯‹¸ËžME8€úî¤Þç k«Š°ë¢qÊ|ÉŽ'BÙèzFÅ2«´Ø„åáE«è@ԗמßA4¾Îo^E—ùòe¿lÙ²OŸ}öößmÛ¶$í¦íd* áH8¿fŶ©ÂÔ¹/_¡€e› ®Üwc“[UoÒŸ.=I˜b”Ý´m Žwv@|(.u¡®®î£»îºË[?ð&? “ 6|úg?ûÙÚãÇ:èPFõ«_}fõêÕ½€¢AŠMÏO?Â]T.‹j‹¶ƒ wQ?‘uÙDmE>Šõ½T0.Œ¶/âîEU‘e•Ðæ÷E/¾Lè{Ûgk*¸ù¶º1ËŽ u² ƒÈ‡±Ï{î¹ç÷ï¾»ë²þþþSRÉ8"‘ükÛDé)~·µ¶T&¸¾õÂ2ö”â:ÿ´¦í˜õ'jkZoÜ•«à<3Û¶!‘HÀ±£Ç í¤! 'V®\ùúœ9sR€Bn²ã}&º?þøÉ?þñÿ²±±q•ã8U&mÛ¾âŠ+þõ‘Gù°´ÃD?BPe+½*UäY'ØEeºH9_&ÚµS uU½È^´-³aÑé9Ä'£ ‰«Å([/¤|½Hà«„½ŸVdË^Èj:&ÑD…-W ~Ù¤@<Ž<®¼òÊãçœsÎkoüç÷¤Ý4ŒŒŒd„;aD Hn[d£µ•øå#˼r>oœK—Q]´É ö¼|ðَܸ ÆÊÕ³eÞøu~D©1Fç”í#7v¶ždχÇÃàà ̘QÿÁƒnx 0·}Ò³sçÎÊM›6Ííµ×>ûÑG­ŠÇãsLÛBœÏ~ö³?þùç·–rŒò ¡˜¢ÎÔWÐÈ».Ú$êÎnÝçSeDã7©ÙóÇ(k#³CŠ {q*/fÙr‹)7ÞlŽ;0mçïƒ}fÇ%zx>dëÁS-_PxlüñˆöÁ Œ'góÈ#¿~ÓM7×ÞÞ~A"9CÃ!¨ª¬5ÌÁêå_@ÉÖƒ ¾0:,¬ÒFÓ^;¹Øgv@9ž‚6>Æ/ò#<§9A.™ ä¥ÈØÐÓÛÍMMvÓPVVÖwóÍ7=»dɩÀl“rï½÷^õÇ?þqM*•ªñÕgéÒ¥/ìØ±ã9."RH)þNüF’uå~Å»êY%êE"[VçÇFg+*×#¿Œ!B¥”òeþ…‘‰T^ ³6¬ –µ#PøR‹ïSÛ¿h¬²r"/²!Ë—/øò—¿üó'Ÿüñ§’©dõðÐD"(++JižÏ°ñ;†ú@§ |ê£ÿÙza=!ò%Å¢~lçDÖ_P!L¥àãƒaxx€,Y²ä•õë×ïL‘™ XõõõºµÙ YVò¼óÎûÙ¯~õ«ç 󷎯5‚L*]à§Nu={Û²}p—•«|èìùq¨&'ªr?ítuHøå YAËG¥yD6"q.‹jëET¯kÏ— \Ô†ŸD°íT"]w|^ûè£î~õÕ×_ûè£?]ç8i2Ð?ái!…ÂñžsH ·)#™maþº(MbUÐ*O>XÞ8c¯˜(è„4HŽÉï±(s÷©¤^ô@þñúøtww¥Nš9s×£ßùÎ Ñh/H¸W]uÕÛ·o?ǘ4ˆD"+V¬xvëÖ­/—zp‚äúyj"Pev:±®²m{û~nÈ$óË߀ 8™çíE>Tðº¿çJ{Q§É‹éç d:ô;ƒ-{¤z¸ÌCæG´-j'ó#²= Æ÷È#=7cÆô÷(¥L&a`` {GÕL.¸÷œyî‘©µ!v…u£>(ç¸>ù¾ó Š2ú›ÅŒÑâÊ,È?K äólÿ¢íÂ2’7.‹ˆÆï¯Þ²,…lhiiÖÖp] ee‘Þo¼áÿ^tÑE}Þ»ø˜˜‡{ÓM7µOŸ>ýÏ`@MMÍ[n¹åÑ­[·nšcÇ>¦âƒýŽT}‡j¿S>uíLü™ÚðÛü³ªpå Øn›÷%²c÷EíUýˆÚˆöem²Q6‚+Ê o9H êH5»ÍÖS® ðh4oËæ­{oß›U²uì˜øüxÑqðÇÂŽ‹÷Ç׉lùãEõ ŽýÚk¯=ÞÙÙùÝ|ðöõõÇ ®®,+¡O»ª/úTG­umdí3Û£ö:þûS·õ•:cP/;fYõ¶7q± µµ öïߎ“‚P(4tñÅ?ùè£ßÛ“5Ç®)Bmm-œ~úéï·´´\L)Þ¨.÷œ}öÙ›6lذéšk®éLA± ÓÅìǤLT®«¼H‰cþÙT°Ëêek½‹ÊTõ<²ãôûÚàw^ ~!1°H*!*Á:Á®úe}ÈD³ªŽ_e†g'š€ð~d“Ñd@d'¢`Rt÷w´ïß¿ÿ?õ“ŸüÁ“ãq ‹Å ΤÍÉS®^zQeÖÖ5i¤¿ yåʱHÆ`*Ðue–eëºÐ|ø46ÇIeÛβeËžÞ´iÓf@¦"îW\±{Û¶m=©Tªž­°m{pöìÙ;Ö®]û↠>ª®®–}ù!"F¦ ŠõwdêGe§›.²m*ØEe*ñí•óÓ‰€èXT>T¿^x×ÔâA²÷F™EÑlÑë³4vAÚûÙç·Ùcá×yçU´Í‹}Ó2SȺuë–>ýôÓÏ´, ¢Ñ(LŸ>ÊÊÊòY{Ù¶Ê;o[7~‘å¸teõ¹}ϼmÈvÛ¶add„ææfH§Ó`[ÖÈÒ¥K7nß¾}cyy9~PMQ¬E‹}÷øñãË F;êëëÿ¸jÕª-?øÁvÕ××;=F9Á VØtBžŸp˜D¦UâY%æùœwQtÝçëÙ}Õ]Xe¾eõ²1¨ž0]¦H°ÂÀ\´äße5¨0çý©úA=¶ƒ<ƒá¾©IÐ~íÚµ+6¿úê½ñx|>@4…Xm-TUW!$wѪ0 œÙÉ/SD‹Y{‘­È_þòüeÛ˜úÓö§ic46Õ˜ø²ìäÉË»ïëëƒ?ïßÇŽË^›`œ{î¹Oÿê•W~^__ëµOqV¯^}åûï¿ñÉ'Ÿüþ…^¸ëþûïo̾®ø„ ÅC/E?~êüFÝùm•PçŸuÂ]$¦UBÚ±/Õƒm+ó+›ê¸Q´ÍáÔ‰a•°ö#ÄuÂ_ÕFe/{‰t‘ø6ä2;*¨g·ù™¾ òío{áã?úÑïìêº Ù­¬¬„X,g”ˆK‘P¦ªzAÔY&®óê} e¥0fÆDÌ«Žß­î\åìH推CCCÐÜÜ ÍÍÍ022„ˆF£í«V®üç_mÞüZ¶)~PMq:::B###dþüùºœ œRwÖÿ]ÌoËúÖ•‰„¼LÜ«"îì¾LÌ›î›s?íuc‘KÁ6Š÷âÀ w¯L'–u¢@q„¾j_46ð±¯Ú6í*[ÓöB»_ÜT÷Àß¼­©éЗÒét€P8 555‹Å ‰ä.^Í[:R”"x–•©ÚË·i™Î·Ÿú ¶Þ/–eåÎu2™„¶¶68|ø0 @:˲ܺºiïÝxãÚ?þøã{AñC©…{Ð6~¢ðºr“g?x]êŒßˆº‰°õ§:¦¼mîÅA&ÜÌÒStÂ[%úMĹÉd4u2{Þ†·õ¶½èö[¯À÷÷÷[×\sÍ•ï¾ûîÍCÃçxQöH8 µµµPUUåååyË%ÊD¼2"n(nƒâb—ew2/ˆ¨žIgaËò^Tƒã`Åz:†øÐt?ííí¹õÙ)¥‡{/^üÒ÷¿ÿýŸ_vÙe}€ ‚L$¢ïV`TիļnÛ¤L%ÖƒFÝä©2|H¨ËÒtDýðõÂcEÑ^H´-3ñò…³Wç'¯‹Èë"ð¼O¿kÉ£pŸ@XážÛ¹0 o~Ÿ·W }Ó‰n,¢gÞŽ¯“µùà "ÒEeº72;>ºaÆEÏ?ÿüU---_H$'yÂÜ[Ý$Cyy9”••A$H$¡P(A6M?ñöUâZäÇïD@æ;ˆp× {I?®ë‚ëºà¤R0<2###Ç¡¿¿††† ™L‚ãŒ.bYÖÈÌ™3·®X±âÕo~ó›ï/[¶lpýnARâGôñß­A£Id]d/›èÒITv~¢ñ¦‚ÝᆰoÑñä•¡p/žp·AœܳH”—rßÏX@`cR&Ú— ó "Þ¯ÈWú:~ü¸õì³Ïžôì³Ï^xèСUñxüôt:]ÉÞ$ÆÝ죔Êïx÷諒PJsBž­¶m{(‰khhxëâ‹/þÍý÷ßÿáé§Ÿžòê‹2A¤øùŒ6‰º‹êtb]TæW¸›lãì—žh_Ýçë³Cá^dwo[ÙÖ¥ºø‰Èƒm¾LÔ—èXLëARnª‹7âã?ŽüÝßýÝÒ>øàÌŽŽŽ3†††Nv'æ8N•ëºQÙ)Ô¶í˲†lÛî///?RWW÷çÅ‹ïùò—¿üÁwÞy|¢ˆ ‚ " MÚ˜ˆye´YQ®K=Q¥£ðé6~/^•Õ‰|ÉìEãµïÅ‚¸‹"צ‘nÐ {¿ Ϻ2ѾŸh»ªÜ„b„Œ d#ñÛ·o¯Þµk×´¶¶¶ÚžžžÊ¡¡¡òT*ʾ¾ˆBˆ …Ò‘H$‹Åâ K–,é^µjUßYg•€ÑŸ^ñCAdòô³Z—£*²­‹ÈûIQQ]PêçbS?i2¢}ÑØ…{±à…;@¡xæËDuAÄ|PŸ&Ï~ÊDûªr•Ø.vîˆ&ljÈ1úàAAÆ £“RöaZ$uFiçëuÂÞo.bîW°›ŒSÔP¸ ]*ûGË@¶Lµ ÿ‡'Ú–Õ³ðåª?f¶N4vÖÖëCV'«÷Ì,™?G2¿ø ‚|Ò`¿‹¡nb#j£î¢rÙ³·­ò&ý˜ü BVïë5¡”ïcÇ$Z$Êu¢ÝD¼‹ìEÂWõ†QM&X?¼ˆW‰oÞŸÌN'öuoNS!®úcFAO:¦ß{ňÐö&B]dg™×EÛeö¦“]™l¿Ô¿† DÂ]Ý• u€B‘Ì—±¹Âl Œ—«‰hÝD‚µWM$x¿¢ êMĹ â‚ R\tÂÛDlú­u×m넽©¨— x]{L”™Œ¶V¸ë"Ø|½ßè»LxûÁ4F„,Ú¯²åûÕû‰ºcÎ9‚ ‚”“èx1Úðõ*Qob'Ùº_áM"æºr•M`# õ K•‘‰tÕ>€\ÈËf¹¢È>ßÞ/¦¹ï²¾d¤úù(E:Š~AäD¥"p¬"Ô$òΖëD½Jä›îö¥8¿yÚ sÜ‹ƒßu¾UQj™­.²­ºèT–’Ã×ÉÆ¨»èÕô‚W•¨Ø UAäD¡TB¯Xé A½itÜÄÎä?Ñòb—P´^¸ëÒeØ}àõ{Ñ‚¬oÖ—(ÍE&ôAb£³ Sá\LÍ_€ ‚ ' ¢ `1àx w¾Ì϶*ZoM×EóA)%(àÇŠŸˆ{Q/» T–û.Ò*ÑíùQõËd¢a2)懊G©ÊBAÉLQ„£ÿ õ"?û*q.+7í¦ãQÔó‚½8¨V•Ñ•ÉêeÛì¾Làˆß(*ï•«úUWUæÙØ1ZŽ ‚ G)£ô&¢ØD¸óûªh¼iTŸ·Õõå÷<ÛcŽ{qšã®ɲ;Hû‰–³ÑvUSá,šHðu²z•O¿iC‚ ‚ ßÁc‰ÄÀñ¢2•°7ë%Ñ,(ÞÇŽJ¸ËÄ®L¼{¨D´jU]Û^µÂ¨Óœ|¾?AD8FÛAdâ*MÛ5êÎïÈû­3í[5dÑEÜMS`tmDbŸ·—µ0› ˆ„·lb¡zÓªD¶_Ñ^ŠœwAA‚SÌt Qy?i.:•½ ²è|P¿¨wJŒé:î²ra/k#Šxó‘u•æûQ tѱ™ˆlÕ˜Líe`TAA‚Q,ÁXÌ”š ©4A¢á²ö¦i6AA‘>xÂÝ4‚®«W ~U„\U¯»˜Õ$ Çd•LÓjü ;Ï‚ ‚ä# Ëw1lÇš6£Û7Mg1íc‰Þ#ã€éÅ©¼8g1Y_ÕÏR‹ª4“œz•¨– c¿åÅ~CcJ ‚ ‚ø§”Â]էߺ ÑwY™It^%ÒýDâMƆŒ#A.N•ÙùI£‘áGÀÓÖä—ÞW±Áˆ;‚ ‚ø§ÔQb¿AmL…¾i4ݯ`Wµñ;Ù˜"B(¥è£Îª¼pS;Yꊮί­¬?mLý Y*‚ ‚ rJuŸè¼wU[¿bÜÔÖO_¦¾ðFLc„¸ûÉeW!KYÑ­þ¢J‘ÙÊ-'ÉÖÉi/õ›m"~òCA©Šß(p±ú«}HûXÊMü›ˆv?¾‘ÀFÜóÊd¶ Y½[S›REØÇ3ÒQuA) ã-"ý f“v&:ˆMÐ4?6ÊrŒº'¨p}?vA„¼ŸúbµAAdòSÌèúXm‚Fáýú2ñ;Ö‹lQ´‘b wQ¹ßȺ;¿¶*‚Špï‚ òɤâÝ´ßÕ\üŠö “b¤þŠ÷± î¹rU[?é2~ËLëƒÖ•ü‚ RZ&RM ÚÖpöe×ÉwN< ÷àø½sªWL½l%Õ‹"ºSvQæXVZ‘!È ÅÛø†EA©E1#êAÚ•:Õf,ý"ã„q'à?Ú]¬›•êBÒb mŒ#‚ ȉM±ìX¢ò:¿¼”…0âÝSýDÞMËù:Q¹(/ë_„iDÝTã AAJ_ê%"ƒ¶AÆ]Ä=ÏÎÄW‘Ú™´K|¼"è©GAÉÇd[ó=ˆbçÉ›¶S{Œ¶ þLêè¶îMÅŠœ«Pý c¼n¤äoNAA&jyI{?~0?ÁˆRe‚¤ÇÈìx[!/³µ3}ƒŒ‡`Çè:‚ ‚L]ÆCtNô®A}¢`Ÿ$YUÆ« o+²½Ð~}ÊPMŠMUjA™<Œ÷÷x1û+E„¾”~‘€°9îJS_AÇPBûR1YÆ ‚ Hñ™L‚´Ôè›Ö ¶IDAT¼ië»Ìo;&Â]Ú®„öãío²÷‹ ‚ ÈÄ1Q‚³TýŽ[ÊŠõâT¸ ýL@ÛR3™Ç† ‚ ÈÄ1™éXÇV”cCÑ^|r´â=Ïß$ó… ‚ òI¤Xâ¸h"{éŠã" ù@}O“i,‚ ‚Lm&“p-éXP¤/ë8ûb2•ÆŠ ‚ È'—)%tQ˜O.¦Œ bAA¢ƒBúÄæÿpˆ®k@IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/transparent.gif0000664000175000017500000000006112203711415025062 0ustar takakitakakiGIF89a‘ÿÿÿÿÿÿ!ù,T;pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/favicon.ico0000664000175000017500000000257612203711415024170 0ustar takakitakakih( " A>;)'%+¯ô=¿ö420520ÿÿÿ;96¸èú „„†µäô—àúGDA/-+LHF+©é\XUtØùååæììì,BO/Vi531WSP+®ñ·çø§§©OÏù--..-.QNK       #!!!!!!"   $$$$$$$$$$$$$$$$À€€Àpyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/pyramid-small.png0000664000175000017500000001560412203711415025324 0ustar takakitakaki‰PNG  IHDRÜ27µsKIDATxœí{˜Õ÷?çTU_æ>0ÃÜp„ŠD(ÞˆšhVØ,."»hbÜUß}³kÖ˜¼‰˜ˆ»„}ˆó¬1’Å Äý0sÙ™°íÛ„ilÛÎe·Yá8yIkkûÅÀÚS=ž!|ú‘3ÂM™2%lãLU6(ÇùDm9®p”"³œÌ(¶Efa.¥’6\Åá†äŒp0,d;#lÁ'òH:J H CÒÞÇ02Iôd]ZŸÂWì(gì€4„Ó¥À?Åèé¿ùŽ]Ü|›àù\ ,g„Êr‚*'¢N*<òxéîŽs×-—pVþFîÌA ÙCx)ï<Òy'2 :GåŸÀ ü°€¿ÆU@!tÞlÞ~ ´°3 pp!ð$ðǶ栟‡"•pïsÀ€CÀ†ösÂÈ¡ ׅ턲xiDF¡®&¨ô]ëìŽ3ãÒÑÌ›y>Öá5Ì»Zò‹W È )"y~z[‚äQá#¢Ð»Ž°Š[ü¨è£^#ð°| !¸ øw7ÿ9àR kí(÷¼B šv¬ 8þŒ–rÛ2Ø §NÇq~#|*±Ÿ¬ãQ)w¨«-áþ;'aÒI¼[ð÷×·°iW˜?n1´A'ÈàyÑÿ ýÜîG?«œyñrJ8”Êê¼H?iDâ´PÐäŸîºˆª2I¬³ åHò’n>ÎKÊ8Ö¦!ídÜ Met‘èã$`#°8­l!pšdÃÝýÕÀ®“Õé§ «€ï¢Uô:©Ÿãî6]ú :d®:êêòø¦'¿”)$†ÔI ‰”Ú¢“‘ØÚÜù7ã˜òÙâm º@YÄ¢ÔU:üãM],Ó41M Ë2±,ËMX@ ‘‚ Ë:Yk8³-"ø/à{n¾˜y2:û”¢X€¶±Þ8µCäÔ†ð—x:ãæ}RÈ'á¢QÅ5Ó«¸ùújâ](倊!”ä‹J®šàÐôyÁò5‚Ád[Bÿç 3‘Ü÷õ§ð ÃýÀKh½8Ï-@pÜÔ—Zc‘T±¼7³ ù ã$£$Unj"Ú©BK\´ûúè[ºý¨´~*j´$Ú Ä2œ[áÖ‰¢m¥Þl2ÿõô瞘À(´—±­9Ä8VUdAÎ$œ‡„ä1MÌDÞ hi¤%Q€` €”cÎ.æ¾[ë0T åDAÅ@Ùhs( *;–Ï-W+.9/€4B„BAB¡¡Pˆp(D(&‡ ‡CÉ”&‘ƒçÓt¸yÏN OëÑ^̪^Ο¼ãÖ½ßW~6Úíý.0ÈC«µ€ßS|uÇ»åÝ:ÞCKáK{éÿónß«Ð/:àhßïÝck/øÎ©E«…ï»ã[öØÞÜK?ån ©d‚Üâö¹ÁmÿOÀ:·}ÅiJºœI¸®®.By…zU—I)¤“ôåµ$Ï’üÃWë(+5ˆEõ‹U @Ù(Bèû.‰c0wÏ<ô\˜Ö0 é“jÚn“R‚ÀuܤØö K7€2ôD8èÝ´£åFwÿ:àçYΟ&Àƒ¾r‰&cMØ»ù¾ã–»À"·ØY£¿®D»Ò_Îи--§cÐ/‘(Žo½à¶µxh@K¿n·Î…À³èøØO3ôp¯³íAÌ„°{î-¾²f÷?ç¶ÿS´$>íCÂ4$á~ÁûÉ%¤&‡ô•Åm˜?³Œ có‰wÛ>w•a¢dGJÀ ¦$5e‚¯]àg¯0M‘ôˆ¦lÜr¡ çÄAõ7héZ"xx¸=碥Fú€ÂèI ðZzˆ¢'tM´Ùh/>FK2ÐoûçÑRu'šð!’®ø à_ÑRèXÚÚÝ­‰žÌÀ{€•nÙW€oºc]„&b-ðÀ¢oý_ÿÏísðŠ;?Iu9a~H’loßGKi&ôCÀh•ô´CNm8SJòóóQ(—\®Äq&š|‚X¦_Ϭ«†aÇHPàDAuC¼:÷ãÄÚô>1Šna0¹\°iT9O¯-¦¤P«–yyyäå… ‡B˜–…ÿc×h,“Ù1 db®DOÆ{Ýý?¯ùŽo~̦¢Õ¾ô7û…Àùnþy’“’vŽ7¡ß¾Œ~ã§ã?2”Å€_£¥ãOÑiÚæôóÛÂh"ÍJ«ó-´:øUà³nÙͤÆ¢_,¢íÇ™À’ cê £ÉZ5þpÔwü5´:º ¸èÛÎ rªR†AAA>Ji/¥!µÄѤ8JRQä֙ŧ 'v køa°£œã`·c·´ â¸î}´àÐmÀ_6ÀËkjùŸwBäA¡`‚‚JKK1b•” î滑Æ¡WIA«rõÀ ´ªÚ9qI—´‡¥è FOÂtÂ} ­;wÐsù‘ßV‰¡%J&²õ…U$%ÞzÎoý:ÃqÐÒùw¬o+2ÔY†–Œ…èɉâ«hõÖF‡Žf¨sMîœ,Õ:Qä”p¦iR\\Œã(M0‰K:‰eš!‰Åº˜÷ùc«Dìû (×ß ´ú®ï$†–!.Ù”Û†°?øë=ÌyìZ"6í´··ÓÜÜLãæF‚ eeeÔÔTŸ,)7½) -ÅîG;ÒñÚ›Xü5ÚéáÅŸBhÂv4fhÛǤª«½¡ý‚¨GK›*’o²>Îým–òÝè—I)Z•Íä´øØ&\_«sÒ"éØÙ‚vdûîXŠz©sJ[ÂY&%%%8¶:îf¹*^KK ï°™[¯ÙM}É¢‘ˆJmL9¨x’pB¸«T\òuÇ¡®|y;w.«G!É€mÛ477³¿¹™x|À„óOª½h±­‚BÛ\¯oõÒÆqô*”ïç‡B¡©kQTT49‰Œ(**ZÞÚÚ I 1}úôð[o½%”R˜¦¹?‹¹7,#„eYÅãñ»Ð^njĒRJÛ¶¥Âoù¯³=ý]$í®löW”¤·6Û8³aI’~HïAñn7vÈéJ+`QZZ‚m;˜¦‰aìÙ³‡õë×óç¶ñÅ©qæ^! €ö8_…a$q¤ vâ K!=Ò è´áÚ±ðw—·ð“Õ•„ƒ*ÕQC2‹FûíBVJy±"ãöÛo/xê©§„ã8äåå½ßÞÞþ÷ÍÍÍp8l{±+ íy3}Ép“ ˜Ë–-;úµ¯}­;‹îCO,9~üøÙo¿ý¶QPPptݺu…hÇJâJ}ôÑ’3f„ººº¨«««B;g2EΜ9órÇqn¡P¨«®®îƒúúú=ÕÕÕÇÚÛÛ+V¬¸!‹'NœØÌQJż¶¾ûÝï6,X°€ë®»nüÊ•+g ÉG«²öO~ò“a÷ÜsiÛ6åååE---gùöOÜ[Ì›ÛòÉ ø–u¹a˜+ ”*GÛAôà ¦% ýÀ-/Ýwß}…¿üå/ó#‘£F Ì®¬¬ôŒÂô‡îßO™LóçÏ<òÈ–ÆÆÆñ;wîœÚÔÔô?Ѧ¦¦K.¸à‚??ÞB«bÞùÂ4Ͱ·HÀ0 m¦;oŒGyäÜ—^zižã8Œ3æ£'Ÿ|rÅå—_ÞŠûzÚ¼y³õ«_ýêºX, ‡Ã^ˆÁ›üƈ#ª½ÆjkkkÐö—w\râĉE–eY¶msî¹çŽF{L=Ò*Àž3g<ð@éáÇ©ªª*ß·oßL·N7ÐýôÓO}å+_1lÛ¦¸¸¸àرcUî±ØÅ_l¾ûî»Þµy÷!NK%ä˜p¡`ˆa¥ÃXµj+_y…ÖcdžI~ždñ½6UÝЭ0Œ6Ž·Ei9;÷±«¥‚ãñQXã(¯¾€Q]€yI€[7òûèÞûGÂ]pV~+£J5 ó `€aÀÿr7óžj ù¸‰e$%›”Û¶©®ªªFÇÃ|2²TÚ˲‚ÂñBÏ18ƒ9kÖ¬w.\8þøñãÅK—.]__ôСC#¤”ö¼yóÞuÛõ·-ðM,¥Å†M†É¶lÙ²KÇ!G–.]úÜ´iÓ"$ #Ž;–ˆþ;Žã¸Ç„r'!ylý¹~”Ti$mÛöëåÞêïå#Ca†!B¡P½J$ÑFCCC¡GÚººº³Ð×(`¯X±Âœ0aB<‰PPP0¹±±qFMMM­¢v¢UÚ. »¾¾~øÖ­[½0ÌiÏ)á"‘O>ù$o®[ç®›4±Å?ÞjpNíqÞú}ˆÍE4·Uc„ÏeDÍxªÎ=ŸË¯:›êš* óSÕþ‹'}ø[:£°¿ùû÷î¢i÷G¼½kÑ)ÛY¸›1åðÀuøÖ‹µØŽp=¤Ú;ª”ðTR…ž$ý ˜ Û¶mw¢cÛ¶·ìi oWçî»ïþð‰'žh>räHåo~ó›q›6m:¨”ÕÕÕ»n¿ýömô\=!DZ=õ, áľ}ûÌŒ6lXË´iÓ§µc´´´Xñx<‰G ¹m½:@@‚éäWJ©LäOyùH)½c¸õ£¾±Û¶ƒi×ãõkÔÕÕ‰ššš=åuË—/¿æ›ßüfIò'^cÆŒ¹rëÖ­ùeeeE<MÌn·Ž§æ:îõæ 9#\Ie%ï½ÿñxÓÔÝÚŽƒ)b<óëc¬Þ0‰Ë®šÅ¤K§ñűc>¬ ßm‡pÎÈ ÎYS?ÜJ¤#ÆÖ­ÛøÓ{ï²|í*>n\K@Dé6ò0¤V-…”H¥PN<}` ”R‰ Õ›„骫«™4iÒV­Zõ¥ÆÆÆqÛ·oï˜:uêz˲¼ñeퟤtM!\ ˆI)€¶¶¶Â––9bÄïÅ gÑ¢E—z„s_)dqI¦Ü>3Îñ% )QJ ¡p'ç¶íÒ»f¯Ž1sæÌ7/rG.^¼xæœ9sÖÖÖúíDV¬X1bíÚµó¼>ƒÁ`:Fè‘-%¹¶j‚€ 2 sF¸cÍÇœ²ò"ýC¬Ž“X¼W»óÙ¾¿‰ ü˜Â¢§(/+㬳΢¡¡††FMMM eeeX–•Ò®mÛ=z„ææìرƒmÛ¶±uë6öíû˜Ã‡ÐÖÞN4'f—0%–¡5Fá~­ ¤ÂQŽ7ÉNáuûí·¿³f͚Ϸµµ•ƒÁ¶¯ýëëIÚBéý'ÊôïÙYY™ª««k:|øpukkkùœ9s¾´lÙ²ÿ.//·×¯__ðï|çêµk×^#„PJ)F ’݃\÷¤]õ6‹´MW¯ÝωS…[&üª+É ¾÷\â .üà /¼°¾©©iòþýûÏ4iÒ?Í;÷…/|á ÛºººÔsÏ=7îå—_¾9‹ ·E"‘Ññx\’$˜§*§ýâñ4¡”b°H7(„Súï„Ð+ Š€â+VŒ¹çÞÿkÚ¶üM€’pPûö£±(‡¦å@ lü3+W¾Œ”’P(Dqq1UU•ÔÖÖRWW‡eYìÚ½›÷îåÀ´¶§;ÚR`¦e%<¡†!±,3é™ô–’I‰£Ç„sǶmÛ°m[2p•€Ù³güàƒnܼyód€úúú÷/»ì²#dVsE<·Çé«çÁ|iÞ¼yãÛÛÛ‡½ñÆ_<ï¼ó&‡Ãá¶£GVÄb±ð¤I“VïÙ³ç쌎D"y¤®»»;q_âñ8î1ÿ˜„mÛŽ7–X,&HµqÇ(¼ûå!eâ»m˜nÞõxu ž}öÙŸÍš5+oß¾}çµ´´4,Y²ä[?þx»ã8†mÛ!!Dlîܹß[·nÝ‘Hdt4õIŽo›ž÷ۜ괖pJ© z`¡›ŠÜF{÷ À>|øp@H©?PH,,Ö;©.{76—táC{G[š¶Ò¸y Ê•Ò00Mýí›aš¾u’Þâe\{M¦Í#œr{`„ …BÝ•••[#‘HiUUÕ>!᫦¦fÏæÍ›'K)ã·ÜrËëøÔ¥ôþóòòºªªª¶tvvTWWïÎÖÿÌ™3÷?öØc>üðÃ7îÝ»÷3‘H¤¬½½½xøðữ¾úêÕÏ>ûìÚ+®¸b¶ã8”””"Õ¶r†1bD ÊËË»ÇS$\QQQguuõ–ŽŽŽ‚ÊÊÊýn”±H)©ªªÚnšfgUU•÷)M¢üüüÎÊÊÊ-]]]ù•••“êåT€š|ôèÑ‘#GŽÜ°k×®GûÓ ô/õÚk¯ kjj*ª¨¨è¸æškƒv>Šh4*ÂápÒzýd:–^Ç0 2Ž¥:*JÛ¶q¯ÇëK‘Û·o7V­Z5¼µµÕ;vì¡n¸á š@ÑX,fwtt8¦iÆòóóÝ·ÄHÆýêeBuM øŸtd•p®Zh‘T Ð’,ä–ëoc4¡l´Qêíû€¨­­mµLëx4Ë—=ˆÖ“@é_ϳ¨õP„Éã25ï'\*Ù¼2½oÑh„â’â ýœè‚‰¬0nºé¦/=zt¤+Ý^ìϘN´ÿk¯½¶åÚk¯=HÚä5 ‚x?Ÿ ŽÊp<= ã'œ¨sÎ9ǹãŽ;"$½Ëžªk[–/..N'U6©Ö#ä3XHN)å-T  UAO%´H®ðts…[!± *‘2®¡¡Á.--iÚßÜ\•bOùåÿb@¥ÌGL™~®¿<-Ÿ$o²=Ó09~¼•˦Oû€Ìމ\@òú믿výúõÐÐа桇úà'×È6É{#\‚tô´ËÒ¥V62§ª¤Ó%šG*oëý>‡§§§_¤G8oëOôüxyãÊ+¯|mÙ²§§ç…ót°8Eí“Y÷¥ï«Dfòô8?퓟Ôvô:ή®Nº»ºwÝ{ï½ïqŠÖÞ½øâ‹åßþö·oÚ´iÓu€(,,ÜõøãÿœT;êLB5“ž¤ó“ÆŸÒ‰–t§DºARjy¤ð_eûŽ¥í MJ?áü«4R¤››â?þñ÷ÒÊ•oµ·µMËÏ×q¶LÒÍS“v˜ÌRž‰¬ú‡aµÓD¦J7Oõt¸hçÎL™2uY]]]¹—&béÒ¥UwÝu×â®®®r€¼¼¼= ,øþ•W^yøŒçtA&ÂyÛLR.pžzÙ›'ÒOºœBø$œ§z‹j yʤJú =É'Ÿyæ™ò{î¹gq8™Ÿ_€B%m-™Ý.“=ˆ—J ÏÉ"S—FL©LISÓ€6mÚ´„S#IÄ®]»¬qãÆý¼³³³fäÈ‘¯/Z´èg³gÏ>“É'N¸tâe’nqúV7=$I™$ÉÖŸ”N¶L¶›Ÿtþ­\¸páÈY´è!Ë4JJK°L+%4 ÓHÒ“\ÉV{ÔK'«ŸhRÒÞÖFSS–e=¿aÆ%………§äMçÝ“ùóçO.--íX²d‰÷·ÇÏ45²¯ëM'ôT ³IºtÂõ©fæ‚p‚äO°ù¥œ‘a¿¿Ž’Þ _}õÕ‚o|ãó[ZZf†B¡a÷ÃÔžÒ)Tž=—‰l’¤C¯ÂhkoçСCD"‘M&LøÅ믿¾zîé‰Â»/gÑ<ôvÝ*-ß›¥/ÒeS7’r°C$œ'á¼”‰\þ2ßçžþ9è•h™>WK–,±lÙ²©üŒmÛ%êáËûuA¢ghÑqœx Ø___¿áG?úÑúóÏ?ßûíÂ3u’ÿoA:á¼mÔËÞV—du¢äJÂyΓtÉ•M}Ìd³ù½’™H–-ÈžéÛ±lèÍHÿmˆhÿ{)<àm{#œ—O'WºÝ–SÂyq¸¾.‚´ã‰•æô­J¦çéGùÎ<ôóçûCºlªf¦ör?ἕÜÞ@=ˆ u¼ûk³ nýÁÉ ]ºÔÊ{Ëiü̓iWú<Òù ç»B´!r áD‘-DàÏŸñ2.§È´–Ò?tÕ1lô²MÏÓò! úç¹ì¯3[꫟AA& çå='ˆ_ÒõF¶L$êXC¤B6œH¨ SY ØŸþN*öÿ½´þÎÆÕ;«t.k~äí!;?ÀÛŸîZ¢Ôq‚–ÜYÁ:ßîw·óuËmMŽ|¬<äÈì¡ñ+3ëÝ9ðípâ-nù³V=F|˜IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/ie6.css0000664000175000017500000000136612203711415023240 0ustar takakitakaki* html img, * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none", this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')", this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''), this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')", this.runtimeStyle.backgroundImage = "none")),this.pngSet=true) );} #wrap{display:table;height:100%} pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/static/middlebg.png0000664000175000017500000000535512203711415024322 0ustar takakitakaki‰PNG  IHDR¬º—ƹ CiCCPICC Profilex–wTSYÀï{/½ÐB‘z MJ‘z‘^E%$B °WDWiŠ"‹".¸ºY+¢XX°/È" ¬‹«ˆŠe_ôeÿØý¾³óǜߛ;sïÜ™¹ç<(¾BQ&¬@†H"óñ`ÆÄÆ1ñÝD€ÖpyÙYAáÞ?/3u’±L Ïúuÿ¸Åò a2?›þ¥ÈËKÐBй|A6å<”Ós%Y2û$ÊôÄ4ËÑQV•qò6ÿìó…ÝdÌÏñQYÎYü ¾Œ;PÞ’# Œ¢œŸ#ä¢|eýti†å7(Ó3Ül0™]"ॠl…2EÆAyJò,NœÅÁ24O8™YËÅÂä Ó˜g´vtd3}¹é‰„Âå¥qÅ|&'3#‹+ZÀ—;Ë¢€’¬¶L´ÈöÖŽöö, ´ü_å_¿zý;ÈzûÅãeèçžAŒ®o¶o±ßl™Õ°§ÐÚìøfK, eª÷¾Ùô Ÿ@óY÷aÈæ%E"Ér²´ÌÍ͵ x²‚~•ÿéðÕóŸaÖy²ó¾ÖŽé)HâJÓ%LYQy™é™R13;‹Ë0Ybtëÿ8+­Yy˜‡ ’b=* 2¡(m·ˆ/”3EL¡èŸ:üÃfå Ã/s­æ# /± 7èù½ `hd€ÄïGW ¯} $FÙË‹Öý2÷(£ëŸõß\„~ÂÙÂd¦ÌÌ ‹`ò¤â£oB¦°€ä¨- Œ Øà Ü€ðÁ ĂŀR@ƒ\° ¬ù ì{@9¨5 4€ œÀepÜ}à>#à˜¯Á Axˆ Ñ 5H2€Ì ˆ ͇¼ @( Š… dHI¡UÐF¨*†Ê¡ƒPô#t º]…z »Ð4ý ½ƒ˜ÓaMض„Ù°;GÀ‹àdx)¼΃·Ã¥p5| n†/À×á>x~O!!# Da!l„ƒ#qH"FÖ H R4 mH'r D&·††abXgŒ/&ÃÃ,ŬÁlÔcŽ`š1˜[˜!Ì$æ#–ŠÕÀša°~Øl26›-ÁÖb›°—°}ØìkÇÀáp¾¸X\*n%nn®w׃ÆMáñx5¼ÞŒçâ%ø||þþ¾?‚C ´ 6oBADØ@(!%œ%ôF 3D¢щLä—‹ˆ5Ä6â âq†¤H2"¹"H©¤õ¤RRééé%™LÖ%;’CÉBò:r)ù8ù yˆü–¢D1¥p(ñ)e;å0å<å.å%•J5¤ºQã¨êvjõ"õõMÎBÎOŽ/·V®B®Y®W›¦°©iŠi…é 3ØÌÞLh¶Ï¬Çkîh.2¯6`QXî¬V=kÈ‚ah±Á¢Åâ¹¥¾eœåNËNËVvVéV5V÷­•¬ý­7X·Yÿicjó©°¹=—:×{îÚ¹­s_ØšÙ l÷ÛÞ±£ÙÙm¶k·û`ï`/¶o°wÐwHp¨t`ÓÙ!ìmì+ŽXGÇµŽ§ß:Ù;IœN8ýáÌrNs>ê<6Ïhž`^ͼa]®ËA—ÁùÌù óÌtÕqåºV»>vÓsã»Õºº›¸§ºsîaå!öhò˜æ8qVsÎ{"ž>žžÝ^J^‘^å^¼u½“½ë½'}ì|Vúœ÷ÅúøîôðÓôãùÕùMú;ø¯öï „”<4 ¶ÁAþA»‚,0X ZÐ ‚ý‚w? 1 Yòs(.4$´"ôI˜uت°ÎpZø’ð£á¯#<"Š"îGGJ#Û£ä£â£ê¢¦£=£‹£c,cVÇ\UƶÆáã¢âjã¦z-ܳp$Þ.>?¾‘Ñ¢e‹®.V_œ¾øÌù%Ü%'° Ñ GÞsƒ¹ÕÜ©D¿ÄÊÄI‡·—÷ŒïÆßÍ¸Š£I.IÅIcÉ.É»’ÇS\SJR&„a¹ðEªojUêtZpÚá´OéÑ鄌„ŒS"%Qš¨#S+sYfO–YV~ÖàR§¥{–NŠĵÙPö¢ìV ý™ê’K7I‡ræçTä¼ÉÊ=¹Lq™hY×rÓå[—®ð^ñýJÌJÞÊöU:«Ö¯Zí¾úàhMâšöµzkóÖŽ¬óYwd=i}Úú_6Xm(Þðjcôƶ<ͼuyÛ|6ÕçËå‹ó6;o®Ú‚Ù"ÜÒ½uîÖ²­ ø× ­ K ßoãm»öõw¥ß}Úž´½»È¾hÿÜÑŽþ®;+¯(Þ´«y7swÁîW{–ì¹Zb[Rµ—´Wºw°4°´µL¿lGÙûò”ò¾ ŠÆJÊ­•Óûøûz÷»ío¨Ò¬*¬zw@xàÎAŸƒÍÕ†Õ%‡p‡r=©‰ªéüžý}]­zmaí‡Ã¢ÃƒGÂŽtÔ9ÔÕÕ8ZT×KëÇÅ»ùƒç­ ¬†ƒŒÆÂãà¸ôøÓ~ì?p¢ý$ûdÃO?U6Ñš š¡æåÍ“-)-ƒ­±­=§üOµ·9·5ýlñóáÓ:§+Î(Ÿ):K:›wöÓ¹ç¦ÎgŸ¸|a¸}Iûý‹1ow„vt_ ¸tå²÷å‹î箸\9}Õéê©kìk-×í¯7wÙu5ýb÷KS·}wó ‡­7o¶õÌë9ÛëÚ{á–ç­Ë·ýn_ï[Ð×ÓÙg ~`ðÿÎØÝô»/îåÜ›¹¿îöAÁC…‡%4Uÿjòkã ýà™!Ï¡®Çáïó†Ÿý–ýÛû‘¼'Ô'%£Ú£uc6c§Ç½Ço>]øtäYÖ³™‰ü߯|nüü§?Üþ蚌™y!~ñéÏm/Õ^~eûª}*dêÑëŒ×3ÓoÔÞyË~Ûù.úÝèLî{üûÒ&Ú>||ð)ãÓ§¿›óüìÎçŠ pHYs  šœPIDAT(cøøñã& €ÿÿÿ‡²H#SØ4½ø¹8]E„¶A¢ÍTô¶àÄ&†ÍDˆaSB¬ë±9§%Hr3NÏ $,Â&…¨½´›IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/models.py0000664000175000017500000000052412203712502022402 0ustar takakitakakifrom persistent.mapping import PersistentMapping class MyModel(PersistentMapping): __parent__ = __name__ = None def appmaker(zodb_root): if not 'app_root' in zodb_root: app_root = MyModel() zodb_root['app_root'] = app_root import transaction transaction.commit() return zodb_root['app_root'] pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/__init__.py0000664000175000017500000000077212203712502022663 0ustar takakitakakifrom pyramid.config import Configurator from pyramid_zodbconn import get_connection from .models import appmaker def root_factory(request): conn = get_connection(request) return appmaker(conn.root()) def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(root_factory=root_factory, settings=settings) config.add_static_view('static', 'static', cache_max_age=3600) config.scan() return config.make_wsgi_app() pyramid-1.4.5/pyramid/scaffolds/zodb/+package+/templates/0000775000175000017500000000000012210157153022545 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt0000664000175000017500000000646212210154276025306 0ustar takakitakaki The Pyramid Web Application Development Framework
pyramid

Welcome to ${project}, an application generated by
the Pyramid web application development framework.

pyramid-1.4.5/pyramid/scaffolds/zodb/development.ini_tmpl0000664000175000017500000000232512203712502023104 0ustar takakitakaki### # app configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html ### [app:main] use = egg:{{project}} pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar pyramid_zodbconn pyramid_tm tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 # By default, the toolbar only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_{{package_logger}}] level = DEBUG handlers = qualname = {{package}} [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s pyramid-1.4.5/pyramid/scaffolds/copydir.py0000664000175000017500000002445612210154276020131 0ustar takakitakaki# (c) 2005 Ian Bicking and contributors; written for Paste # (http://pythonpaste.org) Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php import os import sys import pkg_resources from pyramid.compat import ( input_, native_, url_quote as compat_url_quote, escape, ) fsenc = sys.getfilesystemencoding() class SkipTemplate(Exception): """ Raised to indicate that the template should not be copied over. Raise this exception during the substitution of your template """ def copy_dir(source, dest, vars, verbosity, simulate, indent=0, sub_vars=True, interactive=False, overwrite=True, template_renderer=None, out_=sys.stdout): """ Copies the ``source`` directory to the ``dest`` directory. ``vars``: A dictionary of variables to use in any substitutions. ``verbosity``: Higher numbers will show more about what is happening. ``simulate``: If true, then don't actually *do* anything. ``indent``: Indent any messages by this amount. ``sub_vars``: If true, variables in ``_tmpl`` files and ``+var+`` in filenames will be substituted. ``overwrite``: If false, then don't every overwrite anything. ``interactive``: If you are overwriting a file and interactive is true, then ask before overwriting. ``template_renderer``: This is a function for rendering templates (if you don't want to use string.Template). It should have the signature ``template_renderer(content_as_string, vars_as_dict, filename=filename)``. """ def out(msg): out_.write(msg) out_.write('\n') out_.flush() # This allows you to use a leading +dot+ in filenames which would # otherwise be skipped because leading dots make the file hidden: vars.setdefault('dot', '.') vars.setdefault('plus', '+') use_pkg_resources = isinstance(source, tuple) if use_pkg_resources: names = sorted(pkg_resources.resource_listdir(source[0], source[1])) else: names = sorted(os.listdir(source)) pad = ' '*(indent*2) if not os.path.exists(dest): if verbosity >= 1: out('%sCreating %s/' % (pad, dest)) if not simulate: makedirs(dest, verbosity=verbosity, pad=pad) elif verbosity >= 2: out('%sDirectory %s exists' % (pad, dest)) for name in names: if use_pkg_resources: full = '/'.join([source[1], name]) else: full = os.path.join(source, name) reason = should_skip_file(name) if reason: if verbosity >= 2: reason = pad + reason % {'filename': full} out(reason) continue # pragma: no cover if sub_vars: dest_full = os.path.join(dest, substitute_filename(name, vars)) sub_file = False if dest_full.endswith('_tmpl'): dest_full = dest_full[:-5] sub_file = sub_vars if use_pkg_resources and pkg_resources.resource_isdir(source[0], full): if verbosity: out('%sRecursing into %s' % (pad, os.path.basename(full))) copy_dir((source[0], full), dest_full, vars, verbosity, simulate, indent=indent+1, sub_vars=sub_vars, interactive=interactive, template_renderer=template_renderer, out_=out_) continue elif not use_pkg_resources and os.path.isdir(full): if verbosity: out('%sRecursing into %s' % (pad, os.path.basename(full))) copy_dir(full, dest_full, vars, verbosity, simulate, indent=indent+1, sub_vars=sub_vars, interactive=interactive, template_renderer=template_renderer, out_=out_) continue elif use_pkg_resources: content = pkg_resources.resource_string(source[0], full) else: f = open(full, 'rb') content = f.read() f.close() if sub_file: try: content = substitute_content( content, vars, filename=full, template_renderer=template_renderer ) except SkipTemplate: continue # pragma: no cover if content is None: continue # pragma: no cover already_exists = os.path.exists(dest_full) if already_exists: f = open(dest_full, 'rb') old_content = f.read() f.close() if old_content == content: if verbosity: out('%s%s already exists (same content)' % (pad, dest_full)) continue # pragma: no cover if interactive: if not query_interactive( native_(full, fsenc), native_(dest_full, fsenc), native_(content, fsenc), native_(old_content, fsenc), simulate=simulate, out_=out_): continue elif not overwrite: continue # pragma: no cover if verbosity and use_pkg_resources: out('%sCopying %s to %s' % (pad, full, dest_full)) elif verbosity: out( '%sCopying %s to %s' % (pad, os.path.basename(full), dest_full)) if not simulate: f = open(dest_full, 'wb') f.write(content) f.close() def should_skip_file(name): """ Checks if a file should be skipped based on its name. If it should be skipped, returns the reason, otherwise returns None. """ if name.startswith('.'): return 'Skipping hidden file %(filename)s' if name.endswith('~') or name.endswith('.bak'): return 'Skipping backup file %(filename)s' if name.endswith('.pyc') or name.endswith('.pyo'): return 'Skipping %s file ' % os.path.splitext(name)[1] + '%(filename)s' if name.endswith('$py.class'): return 'Skipping $py.class file %(filename)s' if name in ('CVS', '_darcs'): return 'Skipping version control directory %(filename)s' return None # Overridden on user's request: all_answer = None def query_interactive(src_fn, dest_fn, src_content, dest_content, simulate, out_=sys.stdout): def out(msg): out_.write(msg) out_.write('\n') out_.flush() global all_answer from difflib import unified_diff, context_diff u_diff = list(unified_diff( dest_content.splitlines(), src_content.splitlines(), dest_fn, src_fn)) c_diff = list(context_diff( dest_content.splitlines(), src_content.splitlines(), dest_fn, src_fn)) added = len([l for l in u_diff if l.startswith('+') and not l.startswith('+++')]) removed = len([l for l in u_diff if l.startswith('-') and not l.startswith('---')]) if added > removed: msg = '; %i lines added' % (added-removed) elif removed > added: msg = '; %i lines removed' % (removed-added) else: msg = '' out('Replace %i bytes with %i bytes (%i/%i lines changed%s)' % ( len(dest_content), len(src_content), removed, len(dest_content.splitlines()), msg)) prompt = 'Overwrite %s [y/n/d/B/?] ' % dest_fn while 1: if all_answer is None: response = input_(prompt).strip().lower() else: response = all_answer if not response or response[0] == 'b': import shutil new_dest_fn = dest_fn + '.bak' n = 0 while os.path.exists(new_dest_fn): n += 1 new_dest_fn = dest_fn + '.bak' + str(n) out('Backing up %s to %s' % (dest_fn, new_dest_fn)) if not simulate: shutil.copyfile(dest_fn, new_dest_fn) return True elif response.startswith('all '): rest = response[4:].strip() if not rest or rest[0] not in ('y', 'n', 'b'): out(query_usage) continue response = all_answer = rest[0] if response[0] == 'y': return True elif response[0] == 'n': return False elif response == 'dc': out('\n'.join(c_diff)) elif response[0] == 'd': out('\n'.join(u_diff)) else: out(query_usage) query_usage = """\ Responses: Y(es): Overwrite the file with the new content. N(o): Do not overwrite the file. D(iff): Show a unified diff of the proposed changes (dc=context diff) B(ackup): Save the current file contents to a .bak file (and overwrite) Type "all Y/N/B" to use Y/N/B for answer to all future questions """ def makedirs(dir, verbosity, pad): parent = os.path.dirname(os.path.abspath(dir)) if not os.path.exists(parent): makedirs(parent, verbosity, pad) # pragma: no cover os.mkdir(dir) def substitute_filename(fn, vars): for var, value in vars.items(): fn = fn.replace('+%s+' % var, str(value)) return fn def substitute_content(content, vars, filename='', template_renderer=None): v = standard_vars.copy() v.update(vars) return template_renderer(content, v, filename=filename) def html_quote(s): if s is None: return '' return escape(str(s), 1) def url_quote(s): if s is None: return '' return compat_url_quote(str(s)) def test(conf, true_cond, false_cond=None): if conf: return true_cond else: return false_cond def skip_template(condition=True, *args): """ Raise SkipTemplate, which causes copydir to skip the template being processed. If you pass in a condition, only raise if that condition is true (allows you to use this with string.Template) If you pass any additional arguments, they will be used to instantiate SkipTemplate (generally use like ``skip_template(license=='GPL', 'Skipping file; not using GPL')``) """ if condition: raise SkipTemplate(*args) standard_vars = { 'nothing': None, 'html_quote': html_quote, 'url_quote': url_quote, 'empty': '""', 'test': test, 'repr': repr, 'str': str, 'bool': bool, 'SkipTemplate': SkipTemplate, 'skip_template': skip_template, } pyramid-1.4.5/pyramid/scaffolds/__init__.py0000664000175000017500000000511212210154276020203 0ustar takakitakakiimport binascii import os from textwrap import dedent from pyramid.compat import native_ from pyramid.scaffolds.template import Template # API class PyramidTemplate(Template): """ A class that can be used as a base class for Pyramid scaffolding templates. """ def pre(self, command, output_dir, vars): """ Overrides :meth:`pyramid.scaffold.template.Template.pre`, adding several variables to the default variables list (including ``random_string``, and ``package_logger``). It also prevents common misnamings (such as naming a package "site" or naming a package logger "root". """ if vars['package'] == 'site': raise ValueError('Sorry, you may not name your package "site". ' 'The package name "site" has a special meaning in ' 'Python. Please name it anything except "site".') vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' package_logger = 'app' vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars) def post(self, command, output_dir, vars): # pragma: no cover """ Overrides :meth:`pyramid.scaffold.template.Template.post`, to print "Welcome to Pyramid. Sorry for the convenience." after a successful scaffolding rendering.""" separator = "=" * 79 msg = dedent( """ %(separator)s Tutorials: http://docs.pylonsproject.org/projects/pyramid_tutorials Documentation: http://docs.pylonsproject.org/projects/pyramid Twitter (tips & updates): http://twitter.com/pylons Mailing List: http://groups.google.com/group/pylons-discuss Welcome to Pyramid. Sorry for the convenience. %(separator)s """ % {'separator': separator}) self.out(msg) return Template.post(self, command, output_dir, vars) def out(self, msg): # pragma: no cover (replaceable testing hook) print(msg) class StarterProjectTemplate(PyramidTemplate): _template_dir = 'starter' summary = 'Pyramid starter project' class ZODBProjectTemplate(PyramidTemplate): _template_dir = 'zodb' summary = 'Pyramid ZODB project using traversal' class AlchemyProjectTemplate(PyramidTemplate): _template_dir = 'alchemy' summary = 'Pyramid SQLAlchemy project using url dispatch' pyramid-1.4.5/pyramid/scaffolds/starter/0000775000175000017500000000000012210157153017554 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/starter/CHANGES.txt_tmpl0000664000175000017500000000003412203711415022415 0ustar takakitakaki0.0 --- - Initial version pyramid-1.4.5/pyramid/scaffolds/starter/setup.py_tmpl0000664000175000017500000000176212210154276022333 0ustar takakitakakiimport os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, 'README.txt')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() requires = [ 'pyramid', 'pyramid_debugtoolbar', 'waitress', ] setup(name='{{project}}', version='0.0', description='{{project}}', long_description=README + '\n\n' + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author='', author_email='', url='', keywords='web pyramid pylons', packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requires, tests_require=requires, test_suite="{{package}}", entry_points="""\ [paste.app_factory] main = {{package}}:main """, ) pyramid-1.4.5/pyramid/scaffolds/starter/README.txt_tmpl0000664000175000017500000000002312203712502022276 0ustar takakitakaki{{project}} README pyramid-1.4.5/pyramid/scaffolds/starter/MANIFEST.in_tmpl0000664000175000017500000000020612203711415022343 0ustar takakitakakiinclude *.txt *.ini *.cfg *.rst recursive-include {{package}} *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml pyramid-1.4.5/pyramid/scaffolds/starter/production.ini_tmpl0000664000175000017500000000165512203712502023503 0ustar takakitakaki### # app configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html ### [app:main] use = egg:{{project}} pyramid.reload_templates = false pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console [logger_{{package_logger}}] level = WARN handlers = qualname = {{package}} [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s pyramid-1.4.5/pyramid/scaffolds/starter/setup.cfg_tmpl0000664000175000017500000000104712203711415022432 0ustar takakitakaki[nosetests] match = ^test nocapture = 1 cover-package = {{package}} with-coverage = 1 cover-erase = 1 [compile_catalog] directory = {{package}}/locale domain = {{project}} statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = {{package}}/locale/{{project}}.pot width = 80 [init_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale [update_catalog] domain = {{project}} input_file = {{package}}/locale/{{project}}.pot output_dir = {{package}}/locale previous = true pyramid-1.4.5/pyramid/scaffolds/starter/+package+/0000775000175000017500000000000012210157153021275 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/starter/+package+/views.py_tmpl0000664000175000017500000000024712203712502024040 0ustar takakitakakifrom pyramid.view import view_config @view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): return {'project': '{{project}}'} pyramid-1.4.5/pyramid/scaffolds/starter/+package+/tests.py_tmpl0000664000175000017500000000060412203712502024042 0ustar takakitakakiimport unittest from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_my_view(self): from .views import my_view request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['project'], '{{project}}') pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/0000775000175000017500000000000012210157153022564 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/headerbg.png0000664000175000017500000000031312203711415025027 0ustar takakitakaki‰PNG  IHDR4b·Ît’IDAT8í“AÄ C“\«—›SÏn`b‹íºà)4ÁoŸàû9DRp‘hµÈ¯«×uìô½?÷Þ²‡ ”@»÷¬\SÍ=ýÌ®Š3}½÷­¾ÚOÜÕÜåo¼±FŸ5´9,×cå©ïß»'çãmZó&kÌéä… ´q~øx²Xò5†èßE3˜,óçû÷”01]îsÖIEND®B`‚pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/pylons.css0000664000175000017500000001221112203712502024614 0ustar takakitakakihtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-size: 100%; /* 16px */ vertical-align: baseline; background: transparent; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } :focus { outline: 0; } ins { text-decoration: none; } del { text-decoration: line-through; } table { border-collapse: collapse; border-spacing: 0; } sub { vertical-align: sub; font-size: smaller; line-height: normal; } sup { vertical-align: super; font-size: smaller; line-height: normal; } ul, menu, dir { display: block; list-style-type: disc; margin: 1em 0; padding-left: 40px; } ol { display: block; list-style-type: decimal-leading-zero; margin: 1em 0; padding-left: 40px; } li { display: list-item; } ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl { margin-top: 0; margin-bottom: 0; } ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir { list-style-type: circle; } ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir { list-style-type: square; } .hidden { display: none; } p { line-height: 1.5em; } h1 { font-size: 1.75em; line-height: 1.7em; font-family: helvetica, verdana; } h2 { font-size: 1.5em; line-height: 1.7em; font-family: helvetica, verdana; } h3 { font-size: 1.25em; line-height: 1.7em; font-family: helvetica, verdana; } h4 { font-size: 1em; line-height: 1.7em; font-family: helvetica, verdana; } html, body { width: 100%; height: 100%; } body { margin: 0; padding: 0; background-color: #fff; position: relative; font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif; } a { color: #1b61d6; text-decoration: none; } a:hover { color: #e88f00; text-decoration: underline; } body h1, body h2, body h3, body h4, body h5, body h6 { font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif; font-weight: 400; color: #373839; font-style: normal; } #wrap { min-height: 100%; } #header, #footer { width: 100%; color: #fff; height: 40px; position: absolute; text-align: center; line-height: 40px; overflow: hidden; font-size: 12px; vertical-align: middle; } #header { background: #000; top: 0; font-size: 14px; } #footer { bottom: 0; background: #000 url(footerbg.png) repeat-x 0 top; position: relative; margin-top: -40px; clear: both; } .header, .footer { width: 750px; margin-right: auto; margin-left: auto; } .wrapper { width: 100%; } #top, #top-small, #bottom { width: 100%; } #top { color: #000; height: 230px; background: #fff url(headerbg.png) repeat-x 0 top; position: relative; } #top-small { color: #000; height: 60px; background: #fff url(headerbg.png) repeat-x 0 top; position: relative; } #bottom { color: #222; background-color: #fff; } .top, .top-small, .middle, .bottom { width: 750px; margin-right: auto; margin-left: auto; } .top { padding-top: 40px; } .top-small { padding-top: 10px; } #middle { width: 100%; height: 100px; background: url(middlebg.png) repeat-x; border-top: 2px solid #fff; border-bottom: 2px solid #b2b2b2; } .app-welcome { margin-top: 25px; } .app-name { color: #000; font-weight: 700; } .bottom { padding-top: 50px; } #left { width: 350px; float: left; padding-right: 25px; } #right { width: 350px; float: right; padding-left: 25px; } .align-left { text-align: left; } .align-right { text-align: right; } .align-center { text-align: center; } ul.links { margin: 0; padding: 0; } ul.links li { list-style-type: none; font-size: 14px; } form { border-style: none; } fieldset { border-style: none; } input { color: #222; border: 1px solid #ccc; font-family: sans-serif; font-size: 12px; line-height: 16px; } input[type=text], input[type=password] { width: 205px; } input[type=submit] { background-color: #ddd; font-weight: 700; } /*Opera Fix*/ body:before { content: ""; height: 100%; float: left; width: 0; margin-top: -32767px; } pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/pyramid.png0000664000175000017500000010043712203711415024743 0ustar takakitakaki‰PNG  IHDRî©t߸7 IDATxœì½wœÅ÷ÿ®ê ›µ «œ³–@QÂ`‚cls€qÀÙÆáî±§ó%ßó»óÝÙ>?ççüpNgŸ³}‡98°M8L2QH("i…ÊÚ4©»~|»gzf'íjvV»ª·^­éé®®ªîéíþÔ·¾õ-…eÄsÍ5×Ä»ºô´X“·H}®vÔ03ŒaÐ8Üõ;Õ1¥Q ”:f0¯*Ôv0ëL&ù|&“ÙýðÃî:Z,‹Åb±¨á®€ep\sÍ5ñLÆ™éjo¥6ꔺ˜¦E)eדÀƒR*‰1{ æicxÈdÜ?x^r‹ñ‹Åb±X† +ðFW]uÕ—èÕà]‹Qg+­æ*¥0Æ`Œîê*”R(¥0€qÝÃF™Ê¨ß×üìw¿»oãp×Ïb±X,Ëé…î#„s®»®i\Æ»ÁxÜœ¥§Àó¼a®Ùéˆxç¹cÌK(õË„›üÖc¿ýmçp×Íb±X,Ëéî§8«W¯ŽhÝx)Žú¸Vêu(ñY·Öõá!ðB2Æx 6»˜¯¶6œøþÝw?Ö5ÌU³X,‹Å2Êq†»–Ò¬^½z‚ŽÄ?­õJ©å@t¸ëdÉ¢”R ®N¥âsæÍ]°ãå—·íîJY,‹Åb½X‹û)ÊåW]{™ç¹Ÿ×J_ ÖÂ~*£”Æo†/Ä£|ã¾ûîKw,‹Åb±Œ>¬ÅýdõåW¾¯i­—yžgEû)Ž1­u›1fUÊõÚçÌšñøÎ;SÃ]/‹Åb±X,£ +ÜO-Ôe—_y‡VúËÚÑSìÀÓ‘ƒB2¦Ñ磜ö)“ç?ùÊ+Ûû†»^‹Åb±XFV¸ŸB\ºúÊÛ´V_QJµ+ÚGÆF)¥Î‹D¼‰³gÍ|dçΉᮖÅb±X,–Ñî§«/¿ò=Zó”cŒí#ƒAiçlƒš6cÚ’‡;;·Zñn±X,‹å¤±Âý`Õå—¿ÞxêkJéñÖŸ}´`PJŸ‰“Ò»vìøÝpׯb±X,ËÈÇ ÷aæÂÕWž©1wi­çZÑ>ºP œ5{Îìõ»vìØ2Üõ±X,‹Å2²±Â}¹æškân&óeí8WXÑ>:QJÇ ,;{ÖC;wî<<Üõ±X,‹Å2r±Â}™2}æÛµŠ|Òã` ì2Êc ZéIž¡cL[ë}Hc±X,‹Å2¬p&.½ôÊÀÿUZMµÖöÑü¾j~C,þRgç·»>‹Åb±XF&V¸jÆœ9wjÍ[l™Ó¥TÔY3§ÿº³³³w¸ëc±X,‹eäa…û0pÁ«kG}k­í§:Pú@ç®k‡».‹Åb±XFz¸+p:â8úv0sŒgýÚO—Åx­u#F½ó /œˆÅb±X,ˉ wN7Î;ïÒ9F¹×+ ÖÚ~:áyƘ3ŽÜ3Üõ±X,‹Å2²°÷:ãÄôÐs‡Ûl—ú/¾Õ=¦áº .¸ ‹Åb±X,–`-îuäœsΉ‚¹\iÝàyvPêéˆg<<¸(v¦/w},‹Åb±Œ¬p¯#±ÖÖY&mÎ6žÁJ=M1 `q$9+Ü-‹Åb± ë*SO2™ù`¦ZßöÑRªä‚Œw.òÍb±X,‹¥*¬Å½~hãéåJ«˜±n2#¥ªÐÚUõ¦˜×N9çœÆ}O?mcº[,‹Åb© +ÜëÄ9çœÓf±RϺɜҔç5ùåŒÁÀ¢¹ öAg-²´X,‹Å2ú±Â½N8ŽÓh0óŒ„îêŒZª²ˆW`0¿Ï@ÊõD¸Mg>V¸[,‹¥z•Ý,ƒxf–QˆîuÂÇc‘ ãÍiìÕìyiâÑÚ ­¨…P¯w™J)0F5ª’Åb±XF?qàÓÀ" Sd¿ú€ï¿`Þ¯ó¸þ§D€µÀ[€v`2I˜ Ü4Ow°\˱½N脊ÇmÃèÓÒâîy†hD3kj;^éAk(¥"Œ{-OF|ŸìïgŒÁ£=ÏL:©Œ,‹År:Öç•IÓˆö ÷‹€w#Öüqˆ`?‚ˆó€ÃÀç€Và àI`°øß~ßÁ ÷!ÇF•©™hFL#Æ ûD@ñôö¦¸ìâ¥üí'°pZо¤óùûUè_­ËVEþ ×u•­•blÿ;Åb±X,–¢ Q!MH "ïnÞ ¼„ö?ó·ýÐÊw)0iHœÊ#9ˆr-ÄZÜëD4ÑFeN·PJ)zúR,œ7‘¿ï ¦6üžO¿µ—5Fw/4DG¼ê¯ËÀ­åµ½æƒ±Öçc£”ñâ5«Ô©ƒÆ„–fÿ³Õ_oð—ÒíÆCü½ÀqàUàpØOñ®a‹ÅbLC\;3ºÇUzUã_Œ]þŽ"ÏãuÀ3þþK{ÛnÄ"¾¿~>2'ÉäA–k V¸×cÔh’íÕØD2Ø–>üîULÔBú•$ËôqûU­|õšT"N}ÜcŠQùÂ2|W £âv˜\̦ûßÇcýÏ&äeñ?µ¿”z¹xþâ"Ö>D´ï6 /’uˆ˜ŒUÉb±Œ¦ â5À,ä9sqù69Ñi©ž8ò¬Vô7®8À&äº_ <,~…üV¸×+ÜëÈHWiýh…ôžgȸ.·ß²’+VÍ'ÓÛƒç¼T”[¯H²}_#¿|TÑÚ8tu†…w¬FÈô˜pzE¾÷Ì(àMH7jóäÝŒ4¦+€‹ü^à!àgÈ‹ùÕ!(Ûb±œÚLA|ª¯,Ø>X\¼ Ti© bHy¸xòZ[ ¼~ëuZa}ÜëÉ)àk^/¿p¥}‰4¯¿d!7ßxn2çfC&1;nH°t–!xÅL)^ç¢õ¦túRªºTÞÅòLÉÅ–¼óÄü¥^Äëþ»+Ï¿!7;Ð×b9}ˆwÒ_´‡™ü-"ä-µÁAzAŸ&ïvÛ±Ööºa…{ÝH`ð0fx–*áÁ¥ ½})Î:c Ÿúèe´D!“L ÞH&`zGš?gŠŽvH¥ â9RMþô_ ?]±4¡´yé•Ô¹ßB°0 z•B)åï—ë‹7*\¶‡sà8Ò=ûMà׈EÞb±Œ~Î>TEºËç‚•µA#Ñfžzñ¾qOr†±^§V¸2Dö_{ì`èKdhiˆòÇﻘÉ›I§’(<0AhXJ“LDX>?Í7¸¸.d2¾¨.q…u*›.ï¼*,’ªÌR¹NZëŠi¤î³Œ¥†Dù¿´ ou,Ës)2Ƚ X‰u ¹^1òÅxÓ} ° 2öÈøûFcÀ…S{3ב¡ˆ(Óßï¼ú2NæØRxžÁó<Þú¦³8ï¬idzXîÏ€Ñ~yIi®½(Åã¹ï)C¤Qöôk2iDÖ7—$_‡S©ý¼07É£xcÅøÿ—lÌTÓÆñ{‚ÿ­|¯9mHø²iÀg_L‹Å2ú˜<€´“A™¢ºŒ6’À/w˜½¡í¯_žÿ,þ ñÿ<[ךž¦Xá^O©ÔÊZ¾KäY•µ|€õ©”§RоdŠË/šÇm´/Õ‡çy!˜àx%"±²Çã†;Þ˜b×[_æF¨8¦H]_w¥Šïƒ*¹¿|™¥÷—>°_(È~ô£ƒLæqèBb§—ip%ÄŠÖŠ„w›ÈÀ{p»Ÿ×Hw®Åb]ô m6”ì@è¾XdûVà#¡ï_-Øÿ§œnoµa ÷:‘œ"j´ª(&¡ãªu_HÈÄjy%´Vôö¦9cÁD>ó‘ פI%RAe±îúî2ûûV$ó¦»üÙÛ2|ükQNôb‘œx/W~©}•Ï©øõ)%èË)úbe•®—|c†Õ9¼Îüyf/ò &òH“ÿBÕH÷lb=Ÿœt‹Ÿ‡ˆùjPÈ ©ÈL6l¤Å2ºH¤˜g±Õ+Úë„îu¤˜»EwJ¡OÂÆä—WÓßU$X/Öû›+¥H&3Œçãï?—É“šHu'AûÖåÀÊL•jÊ„\ç”G²OqöB¾þþÇÏí¦ýz;„ZB$|7áíácŒÉ}/8çR×£’0?ÃÂîjðŒw: —Úü÷Ò ÷Ò=» ívÕGù2ˆê‡(Ûb±œúüx ¸¨BºÀ½XQiEØÁ©õ¤H0’“ ¹X.Ÿbyi¥Kî×JݯPÙ}á%||&í’q=>𶜿b ™Þ¤¸Çxžø·>ãùϼ¡£"â=M&i¸ñ’7^ ½ q§ —_x~ŽÒhÊH“· 0Uáº䥵ӯ Gk­ÑJe— 7­šÐä-¹¡pP8h%Ÿ*(³ðÚ>&÷“1àbaûðFànªëúŽŸB&f±X,£‡ƒÀ_P~‡n$äsu©‘ÅR'¬p&*Em f׬tü@"­”KS*oŒÉ-Åê $’.—ž;ƒ¯šI¥ð<L ÿè/›Œ ¬ðâæn”ñH{Š˜V¼÷ê ¦+™ü(1ýb½” SM„—ÜR,ÒLÁ¿øVJçÊ G“ñ×µ–%»ˆ¿eÐàIà½Àÿ¡ºÁfKK½Åb]<|qÅ;‚¸Ã$£ÀSHœ÷ïr:™H,§ÖU¦^$À89÷‹b®aÂn(åŒÿwv¿$ª~ß—H³lñD>sÇy4Ç • ´TN çLû®o€Ñ„Ý̕ր"•2Lé0Üy³Ç_~'ÂñC,šßn´ämÔºð<2ø´ûUä×/ûl)EvlRJgËö<‰°cŒ¡„›½¥2Kº |œòÏ1î¿B–Y,–ÑÃOwºyHÏš:]À¡a¬—Å2dXá^WLö3'ªË„¤|”“ʾÝʰè Êêó.Ü–L¹Œimàß³œéÓ›Hu§D˜~íá:d¨Ê&±Z‡2 ŽQN:\p¦Ë{Ö8|é?žHÞÔÙQ«ë\í¹ôObòEy òV(PA#!eÇ2\$fû<àÍÒ.®Ã w‹e4r ]øôpWÄb©ÖU¦Žä{äœ> ·«"ûú;Š”ö‡Ïù™ûSéþÇÙ–u)²=ŒÄk‡÷Ü|ç/ŸHª' x~eƒŠø“.eÝf‚™XµÔËàûËhäVÔ`4Æ7¥¸ñ¢4×®„¾¤øŽcü¥œÜ1h±Â‡|] ýñå¼Âî1¾‡zÖ¯]ƒ’|%oGòÒ¹¼ƒ‰–ÂKá€B—Yü<ý¼‚ßÔößž4ÝÀ(ïç bu_Mu¶X,‹ÅrÊb-îu$ì“s…éŸN…þï·ï¤¬Ì…å”4·—È+U%™ryÛ ¹åºyd Œçf9*M†`=ˆ#ªÕ®B1rüI™|ÿ2ˆD ¹Áãð±k7šÊœÅël ¾÷?§ð)«Ð9—¬?ð—ÉsÛQá¯è¢å„\¤‚ØBÆP*$¥e@üø7Äu¦KéÀ¶!¯‘Åb±X,C„îu#A0{°…ó‚æa(çÂRé»êç…HGUê 7ŽPB¥(¬b v»{3,š;–w¼e±ˆ!ðÐYŸö°XŸQÐuÍŒ\‡)¨“lO»0¾ÍðÁë]¶îuèê…x,—.ïS0& ÿ#”6×,!ÚÚOt wò¾ãl ûßk•/äà ­<<¿C^ Àr2ü'ðAd*îRt3±ÂÝb±X,#+܇œu·: zU~Ô…_Šì¯lu®à÷­©´Ç¸1 ÜñŽ%tŒ“î ÏkQ*ŽúW™¬…BŠØÏE!iP$“pÆxç•ÿï×"’#¡»¶”¥½ÔIî/tv ê×#Q&¯jz8Ä•¦_2Ëɳx¸¬LšÄþÁºÔÈb±X,–!À ÷:ˆZcrQPªî%¾©B³zx›*bËWR4äÆÑ/¿Âº¨À_ñ®7/àÒ×ú~íO{6«‚A©aK|Ð@ØÚNÖZžmÌ„º<™¤âÆ‹]vîpÏZˆFB5ÍZØs¶óP»¨È9…NÈÿPy×4\­°;S~š~ü‚4á²åtZñîKm8Š <-'ÜA\edÀ…Åb±X,#+ÜëDˆdµ¬ hŠ˜ÊýµBx“b2?ýõhQ”Î;¯ΛÂF„¡;áò–+gqËu3É$Ò¾?{vDlá®(ˆÿîùb8‚2:\P¿:øUE¡H»šxÔðÁë ûFxv47–䯄s,s] …{‘ߣŸp/Ù3Q覓ÿ]+…1^nÀ±µ¾×Š$þ­íXá^kÚ€9H(¾qH‹¼™õöÅ–Z)Àä·l%7à8 Gâyö#wZ€ÀT`¼ÿ]=ȹîvSݼe âb6ù}›ýí=È5î^RCPöh@#×mòû‘er¿îö’›1Úb)‹îuED¬vræðBw‹@ær”ý¡õ57œO^~…ŽÞÁÖâ:9›°_£@)ziÎ^2ŽÛß2›ˆ2¤\7dåœÀC"ݘ|!o‚m"ÜÊÅwT&Ü=*׈…Z)He ½Þ·þúǺ!í=ŠŸ²Ê¥)lÏ”s)Õ{Q˜¦Ê<ƒßW: ÂMZN–jDZ#å›K³€³(/€¢ˆP©Wè¹82­{ ¥g‹""jÕ+z "¶ ó ºÂ6/WÈc)p5°˜ø?7ÿ} ¾‘¤±¸88˜ŒUhD®KŒ`àœw0O°™„çidҮ݃¬Ç`˜ã×7°l„‰;*äÑ œ \ \èçÙŒœ{ðäK½HƒåYÄ ìNþ\Û•H$¦ó‘FC3òû+ûàà7Ôw ÉL`9ů3ȵ~©_53-× …üM¬.@ê8ùŽ“Ó]idð[Ð[ <<y‹¥(V¸×‰8É›0Hû‘Eòr1#4ôwù(eÏϯ=Љÿb;Ãù¤S†ö–8yÛ|&uÄIõ¦é7*3‹ µ&Â0Uv]a Ux»’%¢]‘?@V‘JÁÒ9†w__þ…Âõ  &³R¥Ï-lU/ÚK¡ wCjlå‰õÂtyç婵Î^3ãyYWKM¨Å‹yð¯ˆ-dzÀõÔÇBödöÇñeÒ(àKH„j¸øP™ýÿf³˜èž¼ ¸ NÅÞ!ÍÈ5Ì > X\ƒˆö¹ˆXuÊäÓZ_ˆ§$°¸ø6°uu(W_¦ôùÿòkd5—FÄs åÃ6·#–Ü%À[€—€ïßB⛄&à*¿ìóÈYö«)ûMÈX“Ÿw! §¡f rËñ ä|Ž}uˆ# ¶w ×qÒ¸¬†yˆÈÿ°¸ø&Ò£a±äa…û0 xà¹ï¡=%Ü. ˜5¸ÿäMù‘RBéµÊêcÝ23®°0/Ï3(m¸yÍL–-Cº/«]!ŸFl#”ÎOƒBár1äÃç›s2¾ˆ[°‘0‘¯;žÝá¡õ2¸T²*g9_ÎP'Ô¦(7¨U[è^ÓßÝ&´O4äŽw¬X"ªy–õQ>”Ï3ˆø½¶B>ËeÀÃUÕlð(Äâ9½Bº£ˆµ³ZëvÃ¥h*±}%ðUàµU”1PKû"亿±æW#Ô+¡+ñ2y#ðÏÀZ!ׇܥD[«¿¿Ð½dðYàfD”ro.Gziþ‚êÝ•"!UßNΪ>¢~Ÿ^ürO%šò÷1 î\ÃàcHƒ¶cy÷kð^4ÈônX,€îu#™í;¹+òq§¼•\)•5l‹ø.ÜALøÜvSDˆëP>Z+L­H¥\®»l*o½~:™dσ¼€‡…âÜ„…z˜@Ü#JÛøe«®r'ï[ã¥- rãYQdÒâ"óþkáh¯ÃK†Æ¸œ[~ýK\ÓbÖõ¢»_C©H>ÉKÿu•©1íU¤é¡¼+I/ðÄbZî…ߌÌÄúbÑ*Z€7T‘îà¹äÛëRâ8†ˆ¢°ø~b‰Ÿ2€rªa1ð~àFĽ`(Y | ¹¦ŸEzN†‚.D”—î þ¾°p¿½ójT‡ñ÷ Äm©ïEDûü•}!byÿà+HCf(¨fxÿP‡ˆ#O ÷W-9 é%Zü9°½Æù[F(V¸×‹8]«PhÇw›(ŒôB¡°TäŒó*d”»n@RQ6…ÖKä£Ã¯Á±b´·ÏãŒym¼ëMÓˆG©„ñ³ ? MpR²^èçžÅC&\R`™4ä“·.£p³>î’Wîd“?Þõzøûÿp8Þ±Hp¯ ¬ûù¦—ì‘u óH^Y÷¨à\mT™ZCºì+qÊ–àGwƒeÒ]ƒXŸwUQî`Y\\!þ› Ä주p“ßLý#D„M@Õòzà£Ô÷]t5âztCã:ÓGy×­Frç«€·".ck\¹ˆpNÿUd¿|ø;*»‡ ”vÄâþšÑù´kBÎíä»iÕ’(p+0qÁ98DåXFå|ç,5ÆÃ`ð²®ÙTC‹ÒÁ¢ý%¼M¬åÚ_—ƒ*û©•Ê?ÆÑÙ|t(_í„Öµ–cu.G+Ò76ÆûnžÍ„ñqR‰‚÷P Ô!_ßTî7P”?ƒªò…¸XÓ5íïÑ~ÃÁ -‘кïB£‚t¥’ÉKf)n]¥¥¤´ãŸÖ8y‹“[YtDg×Ç?6X‚<'o½09® ¯Hñ¼Âùˆn7T7ŽÐRc¨lír‘]I@ì~^E™Õˆê“e ¥ÝV:Ëÿ@„Q‚ò7_œÜ»aC'Úgðbädš¿ç"¢¶šß@ F¥ûìßÀЈö€YÀß#">Œ|ø"µí ˆ%ú†!Ê8|ø8C'ÚÃ\ŽüŽÇFð9í±÷a@œ îY =þݹM…Vä"2ûùcæÐz¸Œ"Vdc@+w¼a+–´és}—–{GÞë2ô%ÏâÒA9JçüÜ Î7üÝøV÷l}³ >5é”áuËa×a‡ûŸ†˜“;ŸR1é gV ׯŸ›‹É?6›¯êß;dh0¾Êž‡¢>–š2ÈXŽ.ª³Ž{ˆû㔟‰5‚XÝÊЄákD¬Ñ•XËÀ#y¤(/ÜüýmÀ'ñ7T¼€D~¹®Lš^DÜF¢mD~Ï>䯘ŒˆÓiTßÈX|øSj+†ª½¾K16±B~.âæÒè{{)r?‚œk×­ˆz%ñ 9ÿêpD›jiA¬Ò/!ƒ.GÍÀß ƒ^jüìFƤô Ïr]ÇRþyò›mÂZ|N{¬p¯É$FGDü*…ÖN^T™¢~îaÑWÌwºˆKFéÈ(ýdVp äìKz\qa×®êÀM{¾{GÈ&åZÉÖ¥´ˆwå¢ *šuÏ),_ˆ*óÖKᕃŠ-{ñþçž=ß‚khB' ³îBábJ¸Ïôû-rAÙƒÁÁ…3²¢š\Æ<«ãkÞLåaTéâyàQÊ‹Ia½X_e¾á"$¢L9º€Ÿ0ð •„eâ~ô6ÄMf0T+ðÒH”•kÉoN'küoÿýÀ!Dð$é_ÿ/{6b|Ò+R‰÷#á"Xe}«!IåÆÜdÄÒ¾ Äþ>äüÄç°¿},¹s\M.Ž}%Þ ýO¤·á‹”Ò ¼~ ñ«¢'1Ý/E VšË1ïfäÏ¡þ7âÞU-.r=ïAB­îAîã¢Áš‘hJ¯E&‘{ÅTihÖ3´¥åÄ ÷zâ¿jÄ墿_¹¿Á—¥aËp¾P,&γ‚1ä…ž‹oÄÒݯ(ÕOàö%]Îjåí×O!¢錗«C1¡®ÈùŸ‡lnx¢©@¤‡Ý`ŸÛC‘räú¨lViWÑÒ7¯R|í¿4=IñwÏ»fAzÿøÂk×½°N½e"Ø„¿ço–HB&¤k'`ª3‘(•ØHõ.IÄêþ:Ê»ªL®`h„ûeTvaØ <4ˆ¼3”ov;ˆÕúC”à‘(<ë‘ØáI?ßvÄ2^­ËH¼í~žÿ…éøÖÕBÜœö#½w#ÂøŠ ÇÅñþÒ0¨Ê ÔàN$ìe!i°ü‡¾Ô=û]$|à'!]‰Fdêï?A„b! þïÆëá"i‚²—ŒD>©&Л–T‘öTæíÈüÕ`ëý‘ó.w/oòÓ| Åù$¤d¡E¿Ëiîu#Ž Þ—Êà8N¿8îiSn›1~˜ðþÐ1yQdò ç-žç¾ß¹¿MkE*mho‹óî7MaRGŒt_&Ôp𠕨?øT“×Þï5]ä½m4Ç·¼‡-íás ¹È9„\WŒRþÌ« ”G2©X:Þ|‘æ‡ÿ#þÿŽ"/Nzî܃ÿó¯eéž r=-ðá(?¹|ƒ1ºNÄÁó{ <ãáXå~²hà*GÃèCÉ@¬}Dâ2—ãFàëä\jÁÄšZ©þn¤Û} ³X‡q ø%öïB¢ïü 7XÌ‚tEUÃ.¤2ñH&ؾ IDATþ'ç~dÆÄíˆeùfÊ7“ÏAÜž¾we†IQ>ÚÐ<äž-ü}{€BBVVjdD~ÿ'——RÙp1Ò˜)Ö“t‰Ý•ï©£ˆùH#àÏ©¯¼i =ÆÈµ/@¢ïT#ž»þ…Ò  bBæx ¹¶ÌаŒP¬p¯I0"Dµ?ÀÑ/_<‚„| X˜Ã‰| Ý;²~Ù*°i üh‹9K}n¿á-Wv°|q3©„KÎ $0‡ý×ýÊ„© Y÷p÷àðª’ö@øT©u9$å*.=vrx|“GC,×ó­cž•»xÏEá¾|W¦KL1×¥ÜáE÷9ÚÁhWœŒÁúÊœ47ï©"ÝFÄ‚>¶"⤒p_ \2ˆü˱‚Ê‘C5.3ÌB)Öpø9"HÖVÈc † 1ðÿ­{ׂ¹ˆ³-ˆEøÇÔf¼B¥A³Å,ÔG‘(,ÿRáØB^þ çq}…´íˆ Q!‡ëô­” 9q:i8Tjh^‚4’*Ý;§"1ÄÒ^MÈÌcÀ_"Q§;xú826`'ÒøªÁá–ˆîu$ø v´Æ‰8㔥ü²+…< §)6@³XxCô$ ׯjçÚKÇâe¼")Mþy+aaø°÷·)ß¿]7€v0ALJcÀ¸~Z1Š*W¢®dõrè]è…ªúÖpOi"ŽæÍçkŽðâ+Š–¥ŽÁ‰HD9Ôàe; DDëý(u .}y×™Báî_S椹±LŽ«.ƒXR3 ü‘P}åʇXÝk)¢×PÙgÿ!ªŸ\g  …i€ÿ‡X»k\žap¿O5ìB¬ø?¥¼ÛÓ Ä¾©Få¤w§±®þû Ë:Šø\Ϧò¸ˆbei´ †$Òp˜ƒôZ”c*2.d$ ÷+°•èþð•û]dpðW)îÞd9 ±Â½Ž?ÊŠˆÉ2ƒS ¬áEÅvÁ¾âô|«2…ÇøŸÉ¤aÁÌ8o¸|,G‘._!$ÒûÙŠ øÀbX»=ŒIC¦“8‚!&n¼„¬{iÀÅÆx.ö­Óžˆþ@ê*oþœF)’hÚšàª9Üý›&N$4-M'B,%LjÇâòo ÓFú]» êÉ6ò¯}±k¾Î…ÛGãyâ cŒ±Â}phD(ÿ-•géÎìÀÃ? þוfR½%{YN˜ Èl©åºc\ÄMb°ÛT YŒ!VÙZ‹öz ò¼ºLšéÈÌ¥µî.‹RóÄõèdØ…4dÿ•E:ù ð³“,ûby¿é½(…ƒÜÛ È=8RhF& «4æÄ ûû5.ÿçÈýùåçk¡Xá^gŒ18‡ˆ Í^šo9û¬çìØÅEwq—BÇ—™ZC*cÚ4o]3qcâ¤.yÖóìá`çQ ØMZ¸Ic¼Æë7ñúÀK`L÷È>¼#{}c¹KÖ0¥²sÂb?+šÐá“Úw©ñ­øAµ ôuiΜò*o=<óóÇpœP½ý뢵&‰à8±XŒææfš›šhnn¢¹¹…ÖÖ¢Ñ(±XŒx<ê­ñÆóü߯¼>|͵Öx‘‚ –0 é’¿ƒêbOw#~»ƒžFDÿ5”Có³©p_€„ ,ÇzDˆ—uá?„…ì:‰2‡“È€×rÂ]#.‘O6#÷h-ÊüOäïãœ*ÓoGÜ0jQöïßìJQˆæ"½ICÕÃ2œ‹ ­Ä}Àÿ¡ö>üby b$°œæXá^OŒ¸eh¥ˆDÒ‰&SiÒ¥ÿöD9áVw%H¬ñjCižª¼€ôP”‹[¾±¯Käýµ›½õ0"«î?Eâ«×‚4Ò蹑ò‘ˆ&"½S#E¸+äœ*M²Ô‡ŒO¨6 Ò@9|yþUнoåXá^'€ãûRƒSµ ¢½äÒåù¨‡|¹Uh)ßöÂÏ¢þÙ!7õ¾\öÚf®¾¸ 7í[¯M¯=ün×`\ŒÛ ™pbÜ^p»1^JºñKdõ«¦Ÿ!Ç(ü^TDD¸/ÜsñÛ ÂOªp¯„ç÷ÄBo 0n˜¬å=M&­ˆE ñæcì>4†'¶â½Äœ©Ê]±ÞÞ^zzz0æUœH„X4J<§µµ•¶¶6ÆËøñãioo§¹© ”4ÀLv0l~ŠXÜ# ž18§nçlÄ·y±'7©K˜bIŸŠø/Cb/CÂ>„ï#–Ì“l¸q'('ÜAüÒïâäDØ8à ÒA„ûPLúTŒß#bp¤„ŠœS&Í,¤ÑT/áþ2ƒ÷k/†‡¸§rŒõƒˆ·–f ±X í8x¾KMð»y¾{Íi6ÓùÀ7At'–ÄJÕ‡´òÑÞ†ˆŽvÄÊUMŒè0qoù$µ Ñè!×|™n¼ ‘nì“îg!Qjʱ¡‰_ŒðkF®‹L˜*ûçO roG-y€Ú¸W…Ù†X³+ ÷ç¨}/ÊAÄ‚_N¸kdvÛ‘Â%ÈdYåH nJåB€Ö‚cH¬w+ÜOs¬p´£‰D£/=7©P¾è rùv}É“üYט’~ï¾͸†¶VÍ_×θö™¾HNBº“9™Ãx™CàöŠˆÏºÅ˜‰C‘`ªÅü<ƒé¢Ñø¹ã¯÷Ï;OŸg ,õ€—ï½½çÎ=Á×EøÄ÷â¤]ˆ Pö3ۖɸd2½ôöörøða6mÞL,câĉLž<‰)“'3~ÂÚZ[¥‡EëþÑkN49Q>”ô „¿F^rµb"t./“F#!Àà}\/£üྠbý®×ÑNFþ„9ÝTÙHe—ˆZ4kíOß…÷JaLŸ§öD{‘†C%&!Úc$Äs¿”ʳÓv÷ס. qû+¹|YF9V¸×‹GÜ("Ú!‰àùÂ=4l²âàST1Wš°Ð/-ܳŸZsÅÊfæÍhÄM$Å’ž>„I€ÌqŒ×+á³29,Ö–6ÈÊHv¾…=;骂à9®‚2*óˆe?4pÕ„ÜlÂû² å`€dÂð¦×æ‘“ùå:Es…Gp5¢:lA/ÜžL$èììd×ÎÄ7v,Ó¦OcÚÔ©L:•ˆ#rÆór½–Z°qùµw#éBÂ=®¢ü3ób`1ƒ ÓØŽøÉ—ã"öêÅ‹Ô.AæØfÜäp»ÁëCŒ?þ`Ò¡Âx!á^ä0*ö±˜˜G|ܳá!}z…?à_ÌCÚshˆ¥ùë7â•ÃXû2´5T¡˜h/e!/p˜/ìÕÀqȤÓìß¿Ÿýû÷óBôñ‹;–î®.´Ö§ÓàÔ¡ä8ªð_º¸æ !ÙÞƒóRŒnA&(R9÷#ˆå¿¸H8Ì¡îþ ¹ÖS¥ÙH¸¼iþöfrâÝ -S«È·ÜÀÊZ²q«5idD%jÙ#f?Ùð`%i«°ÿTa†¿Tâ·C]‘‘߮ҽlÅXá^7 Ž#"ÚÓºLt˜Ð!a_2}‘¯ýüµvȸivï=ĘØAV/îÆé:B*‘D~„g1BÑŽXÇî*h#„?)¾-õÜ">ìRàÂí.}Iͤ¶>wCïùV G{ 1jrn7Ù(;Åcµ›2פðØB‘¯ù \Ïåð‘Ã9z×ue†\oh¯õ(Æ V§û‰Ncð1Í«eÒ^N¸+Äj>‰G—¸šòþÕ.ð+êg¥L"ƒ‰Ob1_ŒŒ—X ž‚ˆôFê'¸kEŠ¡ ;éRÝßÂPùë@å„yc…ý§ ã‘Þr$€gëP——¡iðYFV¸×tªãDˆF£"àBñÐó––p‡ÉV ¶I—õ§ö<úúúØ»o7[¶î$Õû*ÿô'šÉ­è ,Éu~~šœ«L¾› ¡Jÿmý,òÙï¿m㇊ ¬ö8(åÐÝå¼ùG¸óÚ8þ³"Aç„)bm/¡©‹¥ËÛV$¯ Á¥)Ðu½ò!n,¥8Œ¸ÄÜDVYËÉ…š(?F¬îåÜ)– ƒÇþcùŽõ)'¦62ôQ+¸œ\ÈÉZCë^‹Œ/x •d4 É=ø‡&ïá"÷rN‰16IÔp1i–£“Ú0.‡¡¾Ï<Ë)ˆîuÆ@Öâ®´&/hc%ážu“Qy"W„nnR!×u9zô(;vì`÷îÝìÙ÷*==)þþcí\x–"8EHšÐgÈÂLTšg‘/vlÈBo‚è6ÊŸ‡‹Q©d”[Î;ÈK{&óíGZÃó|òŠÌ³¾÷æý¶»¶á¼CÁ H¸½rS»A¬î¿ úÙI—"Ó”ã—Ô/L!ÈïUÏòÂDËú›×£)ÃTËÀ ‚§lšáF!=g• }o_!§ÀËÛ2œXá^GŒñ0ÆC;ÑX'kq(1ð4›]å¥Fi‘þN$B:bÇÎlÛ¶={öÐu¢ c ½ ޾™w^¯ðÒæÔôÐ0ŸøyC¾'äÚ-Û‚°™&ˆó—´çÐ÷øä5GXßÙÁ3» ÍñPxI“›Õ4ãÆ˜²OÉÜïv•ñ×C ,0ÙüÀȽpÊ¿»jƯBü‘'!>®ˆrpÁ2Hä.àbí݇¸Ä¼Ê©1Ez/5æõ”~v*dë4ª›dÆA„~¹ˆ;ˆ›L­\+ªiPx Ï5_ |i†òO›?Ê!b´ˆJ Z‰½ÔoN‹°Â½þˆhM4ÅU:$ÆC¾í û¨(ñ:ç;â8­4½}}ìØ¶Í›·°{÷n‰ÆGÓݯYå3ïj ª É4#çÑZÔ&Ï]†œŸ|v£JLå€JùŸ.½ ×|œÏ\ßÀßmåX/Ä"AY*ßÕÅø£‚ –÷=”¾X/F~”þÐy^òà(âÖ²i†M±°5+ˆ5g4¤ÓÏY+¼^tÛÒ¥K×nÚ´iƒçyËËd½páÂ…«7oÞüDèЧ o»ë®»&}ò“Ÿ\ÓÕU:Túœ9s6ýêW¿š´lÙ²Õä®›*X7oÃûÃ×YΧ>õ©e_ùÊW¢©Té÷X,ûþ÷¿ÃM7ÝöÕ¯æÉaЬ—Úf\×ÅquðàAõ¾÷½ï‚{î¹ç&×uOÚ®dÂãÿMJ'ž1:ô›÷Ckí\~ùåSxàYHCÒ -nè3X/v^L:5²ÿ~e*¸Ã544¨¾¾>¥²“SÔ„ÓëÉ2t8TaèC3VÁb)‰îuÆ“…3)X*Ìcž•]d`$A;½½½lÙ¾• 6²{÷n2™L6¯ˆVô%=ƶhþêýÌœÉFŽh/Fø•äÛi 7åN0làæôôŹxÁ!þäÊñ«8®§ˆè.Òë1Ð}ò½Ô—Ó†°{L „*⋬°ø,©¥>ƒu‡\8ÀÂÏðRl[Ñå©§žŠ¾á oè|ðÁË ÷ȸqãÞìØ±±íííAC¤Ø¯=º0•J•Œ¹­”ò®»îº½Ë–-[@mD™3f̘±*~ªx¹ª©©©±&™tGïÚµ+~ûí·_ó裮r]w@¾ÏÑh4ÙÐÐÐÛØØØÝÖÖÖÝÒÒÒÝÚÚÚÓÒÒÒ×ÐЊÅb™††† Àý÷ß¿rÿþý³Jå‹Å¢W_}õJľÒ9Šøl£óŸÿùŸÇ¿ÿýïo;zôhɃÛÚÚZ¾ð…/,ÆÌh—]2¡Ï ¹F­)Xï÷ùÅ/~1ò™Ï|Fgߥ±b³<šÊñÛAqÛð`–ºb…{1þ?ÇqˆÄb(­ ÞéY?u¥roy¥òÜ-´Ö8Ž&™L²mûv6lØÈ¶íÛH&“8ÚɦSÒð<ÃßÒÄê©^3²E{@¾gJŽ`W#穌";@5χÞÁ3ŠÞ>‡[Ï;Äó“øéºÍŽÉ^ó~e!¿ªz_9yåöGa¬¢hhhˆõõõµ“ßa1¬ >£þêH‘õbß Å¹ö?s-¶|ªÝ–·½¹¹Ù¹ñÆ·=ú裉t:]òžqãÆ%O=õÔØ+¯¼òp©4étÚ<òÈ# ’ÉdÉpmmmG®¾úê]ÈyÔB ¤¿Ç0°ôEïÚµ+zÛm·]÷Øc]ZíA Ý'NÜ?}úô=Ë–-ë\ºté¡E‹;묳NtttdÈÍÞ ïì³Ïž[N¸¤¯DpϦ×ÍÍÍãÇ)ûnmiii>ãŒ3ÎfSüú÷mÐ Ä|XÜg ¾{ï~÷»Í÷¾÷½/¼ðBÙÊŸy晓֯_?³ ÏÂÆHvŸRªÚ{o´ˆXEuúhÔ<È-#+Üëˆ1bqDbÑÕßÍ¢¸Õ]FH&Sl|é%ž~úöìÙK2™@k-dBægÏ@WŸÇ‡ojå7Eq3§ŽL¬*˜Ê`ayfBÂXhr“4‰qÌ%Jc$Íç®;ÄÁ®É<¸IÑêOi10kz¾?(¿Ô€Vå߯;u~“BŒ1‰üˆèðzð=Ä.¸à‚ùk×®-ë°xñâ™ÀÈ·œ‡{x)Õ4+çvQì{à7_k¼[o½µóË_þòÎ;v” yâĉñßùÎwμòÊ+.‘D½ð ­Ï>ûìÒr…-\¸pëš5k3zQ€êîîV7ÝtÓëÖ­»¨šZ[[¬Zµê‰n¸aãe—]öê¼yóúûÐ,…¿»J&“*NU­¼ûÎTò‘ñyžÉåþйqèx<®§L™2±’p¿è¢‹–#3 ‡{ Ý‚²ßÉNŸBzaŠ.7ÜpÃÔ_ÿú×Ý„8õ¯¡:ßõ(£Ã$fAXá^Oü׊ÖÑHÔ\šß3¬u¾hŒD"¸—Ý»wóä“kÙ¼e }½}(­ÅÊ䢧ÏcÙ¼¹%BÔä0RJÎÉÑ sˆ7óÍE\ÿšhÝ*2®2Ü{ÑCD¼èD:ÂÄÖn>yõ Öïiçx|ös—óù½ç…è,Q±‚ã_÷ eJzO Œw’Â%xᇗ8ò²‰Ù^ŠùIæ™={ö´§žzªì‹:7 ñˉî@ÄœêxãÇ×+W®|±³³s¾ëº%Ÿ¡k×®=óàÁƒutt÷Ýwß´ƒN+u|$I]qÅ/2:…ú«¿ú«åO?ýôÊJ #‘Húì³Ï~êÎ;ï|ø¦›nÚG¾Ïþ©>ûf-(×@ ãc´çU5³[`Qvè•û^ØH »ê8W\qÅÌûî»/šN—Ö¼Ó¦Më¸ûî»W­X±â(Ò& Éжà™Ð¯\ÇqT§:Ø#ê¢Å´02B[ZFV¸ŽÖDcQ_C ô}Ý#‘Z+öîÝǺuëxö¹çéêêB)…ã”6¥2í­š¿|SÇ;$û†Ø°d¯B"Ýå(ðg;íIŽÍй§—›LÓ0sŒm„¦¸îyñÏóç&ʺþP°2Â3ÓêÐc< ZÓÝ×ÀYÓŽñÑ×5ñ·÷Å1€õ„d‹Î³Â–Q _ÎZo Iç½u|^Ì$ü=<¥{Œþ<^°­œEh°‚0ô«W‘XT}á`¾‘Œwûí·o¸ÿþû/9räȤR‰^yå•?úÑf~ìc{™"ç~ÿý÷ŸYn°ä„ öèCÚÄ(´¶ÿö·¿÷ƒüà ÏóÊZÁzn¹å–_ßu×]ÇãA¨Qw=†ƒB!æò“A"\Ub"òŒ=g¶ŒR¬p¯ b ž÷hT&úS*'Û ¾¨Féíëcà xøá‡yå•W€ÜàÕbÖM¥Àõ ©´áη5qÅù’}µ²ëæŠV€ÖàD5ÚQàÈ“„cÝ.{gؾ³Ûúxig’—¶w³igо>hk„¥Ó`ñ4‡ESaé4—y“aB+´6@Ä‘r\Ò!1œLUçã½ëÓ]…t‚qð ô¥o?ïU^Ø=•_>ïÐÚ@ÖW^®;ä½[ µVÐ0(Óe/æ Æ¦L™2öÀ¾= é/¶Ã¢»Pœ~ÛÙÚ|–ÚVŒÑ"¤ë¹òÊ+.Y²dóc=VR¸§Óé¦x`‘/Üèõë×7mÚ´iQ¹BÎ?ÿüçg̘‘bôýNæë_ÿú²ýû÷Ï,—(‰¤n½õÖ_ûÛß~,8®u³ C•nBáô”ÿM#ÈŒ¹P¤`Á‚s* ÷3fLúüç?2'AÒXöƒAšÜxLø»RêHç1y>wW‘¶VX ÿiŽîuÅ€ñp"¢‘¨¸]hñ:PÈŒ§(غe+¿{ðA¶lÝJ2‘$qrÇ—Àó «×ã=×·ð‘[cdR^6ßZTZ)¢1 æ›×ÓGNxtîKòòîÏoîá™=ìÚ—áб ‡Žy44gÆôÙ,9c!7¾ã¦M›Æ¶mÛøÃžá¡í;øáÓ¯é;Âäv‡q-Š…“aùlÍkfxÌè2­Ý£­Qô¶ñ ™´ëP:§¯œ@ò›äÖq㟻î{NtðÔ%e^‡°ÛKx,jIËz."P0y–ça* æÏ¬ U%ü"+õ9ÜIF;Ð7ÝtÓ3ëÖ­;7•J5MdŒZ·nݲ§žzê±óÎ;¯‹œ¥Xýë_?ãØ±cãKÐÒÒrüï|gyG呉Z¿~}óo~ó›K*¤3çž{îSV´Ÿ¶T2>¨j\‚ZZZšZ[[çѶѼÁ·ôìkÞûÞ÷NûÖ·¾åùc~J1Ò¤IS8PrzÑT¦Ò2Š±Â½^$ÀDLvpj4ÍF# f<=~ì8>ø O<ù$ÇŽóÝbtQ {!]½KçÄøÄí 48Šd‚ú“uw‘:¥AÐ6о—]ûRlîLñâ¶^ž{©‡-;ì>àâgÌØ©,9ã Ö,=ƒåË^Â…ó™=kMMM466úâÕ¥··®®.^~y[¶nå…õxqÃFžÜ¼…‡¶Æd’4:)fUÌŸª9k¶fé4܉Ú24Ǥ¾®Q£ðüÅå«_'8%Ÿ!І§ô¥£LjKñÙ«»ù㟵²÷¸¡)\šü¨ŸzaH‹{ñþã[Üý‰°‚xÑ#‘ª,lALmF—øro¹å–_ýêWwlß¾ýŒR‰Ž;6áG?úѼóÎ;ïyrçï­[·n^&“‰–:nÑ¢E›.»ì²W©ý½QµUÔ³…ÕöwSßùÎwœ8q¢d£`̘1‡?üá?ê­É5¨&^ºëº'{ÎFk]Íß¡rjy}‡óo2p‰+‹çy'{o@U;Øu]—ÒaMÃ.ˆa"Ë—/×Ñh4YªaîÓzÝu×½ç›ßüæ#È`ß$bÍO >òÉж td8´§€ûÈX¡á˜˜Ìr a…{ þRµ–™S '*‚rËæ-Üsï}lܸÏóÐþ ÕJO&$S†±mŸ{O 3&i’=¦¬h÷ ¾âîâÈÀQE|_\—®>xõhŠ=¯$Øðr‚ /w³öù.¶ìréM(š[ÇÐÑ1™Ž‰“yÓê3Y~ÖkXqöÙÌš5‹±cÇÇK–¯µCKK ---L™2…‹.º€ÞÞ^Ž9¶íÛyöÙçxþ… lÚ´‰ßï}•_®?Dª·‹ö¸Ë’i^;ßaÑÅâiÓÆ)Æ5§iŠËùày¸žGÊázžQ~ïFØp’oDQÊГŠpÞìc|âŠ(úó\£È;¤P˜—šx©Øvå[é• ¿­Gº ­Jú†±‘|žE™4iRzõêÕÏ–î©Tªá÷¿ÿýbàü?ÿçž{®içÎ%ÝDÇÉ\zé¥/¶·¥ã$ IDAT·1¼kÉ@ò’ûóÉ'Ÿœïy^Ùîþyóæm½í¶ÛöRß&UA;Ü £Á¸«Ô¬ìêÆÅæ•=ØòÛ˜+,¯T>fÖ¬YÇz*wÖ¯_¿ xÕ…y†Ï3pÑ D|qßÉûN~O@6òoΕc9M±Â½žø¾Žvˆøî2ÝÝÝ<þÄÜÿß÷³oÿ>”ªtÕ3Ï2®áConáÚ‹#¤ËŒƒ7Fôc4"¾éDxŠDÒãða—½ûzÙ¹'Ŧí=¼´£·ö²}w†¤i¢£c3¦-áš7Ìeñ¢…,\¸€3Ï\ʼysin®MÏ]SSMMMLŸ>Õ«Vpôè¶mÝÎK›6±eÛ66oÞJgç.¾ÿÜ~Žüî­‘.N‰°xJ„ÅÓOÕLç1uœaL£¢1.ÁPÒCÚ5¸žƒA¡‚–K1ÈÄXsæq~·)Æ=ëZâPhqÏ}WßÛ?Mà¥òäú¨Ò±§%7ß|óæ_þò—Ê Rݵk×Ìgžy¦yÅŠÝ€zâ‰'&”s“éèèxeÍš5;‡¢¾mH Geß¾}Ë¥ÑZ»Ë—/ßA E¥ëº¤R)û¾óÎ{ ÕþæÕŒò,tC,u|¶WìÜsÏ=ÖÖÖväĉÊ•ñòË/ÏݰaC|éÒ¥=ôoŒ„¿GÍÕR°? ·Y87¤Œ1ÉéÓ§_¶gÏžj^¸öå2б²º‘À˜žç‹Einjfë¶-üè‡?aý‹/’N§qœ¹çLuœèñxÇu-ü¯·7àºà¹&;€S)ÐŽ%ÅÑà€IuÙ²'Ás›ºÙ¸µ‡M;’ìÚçrôD†=M­,Y²‚K¯YÎ/fñâ…Ìœ9“ñãÇ1fLæ©)cÇŽãÜóÆqîyçJ¥8qâdçÎ]lÞ²M›7óÜó/ò«û·â%ÑÚmšYã=Lv8s¦fét˜>Î0¦Iu<2ž"ãŠX7PJ“r¡!êñ—×u±÷x;ÏîV´Ä+G)½.ß1yñƒŒ1wH,nõ¤Úºôó,…yýë_påÊ•OÝ{ï½×—JtøðáÉ÷Þ{ïô+Vl܇zhN2™l*•þ‚ .xöŠ+®84$5€EvÜ)ôÚµkÛ»ººÚÊ&ÒÚ]ºtéj‰èرc‘d2Yq&ÌXÁ«ã>d÷ª ]ÙU¹¯Pþ™0û³r¥ŠŸk±–KÞþÉ“'«¹sçîØ³gÏ‚rÑŸŽ=:åK_úÒYßþö·å[¬±P¸^¸ÍŸl„FɳÜïÙ³' ¼‘ ƒSµÖjñâÅã7lØ0 ÿüH<Õ^3Ë©îuEþNµ£YûÔZ~òãŸðòŽ~Å`ÕêP ºº ç.iàsïn"7¥ˆ7*p4x‰”áx·Ç«ÓlÞÕǺ]<ób_Ns¢×E;ÍŒ0…¥+–pÁÊóX¾|óçÍ££c---Ùè7§ ±XŒ &0a–,YÂ5׈˜ïêêbÿWÙºu/¼°žuO?ÃKÛwðÔÓG0k{Q&ÍøÆ g΀e³¢,›¡™=Q1±MÓ7D# Qd ¤Ý(“Û]>{Mwü¨‰ã}Ð…<«z¿©ý·çV¬«`0rÞ3{$ Zãy^U"…‘}žåpÖ¬Y³ñÁ|]"‘(:ªçy‘‡~xÁ§>õ©{öì‰oÚ´i%,~ñx¼çª«®Ú‚¼À‡"FyÕ¿A ü½û•}àÀ†T*UÚΧ­­­R¨À ¶mÛÖT©Á⤄{ÝEJåY‘¡òq¯qT™RÇî50Ÿ…ëŶy—\rÉKO<ñĪr³$»®{ôÑGW;vì‰ööö BQοÜþ°ÿ{^Ãõÿñ—¾úê« JÕ#@k­gÏž=˜ƒÊ—>9†ió#dN¸KzÜfçž$/mëá¹Íݼ¸µ—Í»\Ò¦ñãÆÑÞ>Ÿ×œ;%‹qæ™g²dÉb-ZD{û˜ŸiýˆÅbŒ?žñãdzôŒ%¼ñ1~;f×®]³K¥ëèèØó–·¼e7¹n­©‡¸*™_OOãºnÅL‰D`ɨEÙæ¡‡šrüøñ².ä‹¥A ÷jŽ5>'YÖ Ë‚F àÞ¢s/ZT¨ŒB¥_ªì`»û¶·½mû¿þë¿8tèЬr…ìÞ½{Ñ]wÝ5÷ÓŸþô6òÿvKYÚ ëWl §ñî¾ûî+Òét+Uà‹î`6ê ¯`ò½Àš¾.91ŸÒƘ~‘vBÇYa?ŒXá^G\Ï •bݺ§ÑZûV~ï{žAo\å°kï þó›w$Ù¾»—{ú8|ššÛyÍk–³ìÂ…Üô®E,Y¼ˆyóæ2aÂÆWvðèh¡££ƒŽŽ.ºP¿öõöräèQ<Èö—_fëÖí2vË6~z÷V¼¾ŒoSÌcÁ´(ó'iMëå‚ÙšÿÙæ`tÖŠLógT Ï• )]äòzfH^žõ¤êéÝÙçY3sæÌÔE]ôÌž={JΤºoß¾™÷ÜsÏ”7N8qâDG±4Žã¸«V­úÃøñã]×uã8C)Ü+æ=VYÇ]­uYS©çyzÇŽ%ádËVû÷ïw~ðƒ¬r]·ÚîÓî!Q^–¡´]eÑÙäµ,»Ú¼ î«“)¿ò´©â’Žä’·»Ø!á/‹/îYµjÕã?ÿùÏË ÷d2Ù|×]w½aõêÕw­\¹òÅ-êÅÜhÂi¼ßÕí·ß~aggçåê=ct*•rBÇ÷‹dS°ûƒ;ÁÌÚáýnèÓ\ÿºšÐ¾lyVÔ-V¸×#"å¬ìƒ7ê$z»øéáß~áqäDÐ,\¼”w}àZÎ;÷\,˜ÇĉŒ;–ÆÆ²ƒâO›š˜ÖÔÄ´iÓ8묳èîîæØ±c:|„-[¶±ö©u<úûÇøÑ[ˆ9†ñ-Ç9†ë6¡bMî/¯{h[¿é^Ãn4¹ ˜B¿ýH´é«ÉçY ï¶ÛnÛpï½÷:~üøäb 2™LÃÃ?<§³³³ä Öæææ#7ß|óÀ"ѳ”Õ\¸O˜0¡/–eÒó¿ª3Í?Rù>_ )vLèØR|Z71G‚ucLÄ£ý¥–»Ÿ6X‹{=1¦&s™*ZÛÆÐÜâây®ëâf\v¼¼ƒíÛ¶áyžçÑØØÄøñã˜0aÂÿß޹ǹQ\ùþT·¤Ñ¼5¶gŒß0b &ĹöšÇ5á¹`‚!<Øò!Þûá³ Öönn6$$7χÜÝ% ×$!„…½fI ÄNl60ÄÆ&ÄãÏÃöxÞ3šI­®û‡ÔšR©^Ý#ÍŸ¯?²º«Nªni¤_®†ùóçÃâÅ‹aÑ¢E°páB˜5kÔÕÕA]]ÔÔ˜^³5µp]úúú ¯¯Ï[†ššš ¹¹ššš ½½º»»¡··R)B!ˆemÙPQQ!ÛË e[`YÌŸŠ÷lJŒø.µ’¼öÌwQ™`è 7ÜðÎûï¿ÿ_d) d-ËJ­^½z÷dº¼KÒ9sæ¤fÍšu´µµUz=@GGǼ‡~ø³7n|Se§À~ñÅëxà›zzzf ŽR;›b0¥ñ1)+z×EhO˜í1Ã\<Ï?Ëú/؇ÃäÞ{ïýÏ={öœÓÝÝ=OÓ_ä•W^¹þÊ+¯´^~ùå_Ù™Ÿ×½üpS± ?üáç?úè£wvtt|F}”‚QOŒDÇ*+çÇÈ—‹ì„Câž½ 9ñÞ¯„J)%‘׃Â}¼ ð–,\Á$êµ, X„æüRJfo@”J¥ ··:;»àƒöB:‰Î‡ÃaˆÅbÐÐÐõõõÐÐÐ .„ÓN; æÍ›sæÌ††¨©©H„¿¡Üäcddúúú ££Ž9ÍÍÍðñÇCkk+tvvBWWtuuÁÀÀ¤ˆeA$P( ¡P*+«À¶C`Û6+9'„€g¿M€ð¯‡ziHÈFä@f}~&~H]ÊF/¦*&y¦l”çËÝwß}ðŸþéŸö666žë·íܹsï¸ãŽ?CéÏ“÷í¨4¢”Bö–òEÏ%—\²{×®]A·’O¥RÑM›6]þÌ3Ï||ûí··ù˜Gyä´'žxâÆîîî٦㢔Bæ&›c;ælš€Î†2¹×Åúû'žo­aFûµ%Y¡¬|sQJ½Õ¨LóÎ¥~´ʈA6Z,t¥(£@®»îºc[¶lÙô/ÿò/N§•_Š©Tªâ׿þõW–.]:ïÞ{ïÝüÕ¯~µ‰ñÅŠ[>§ìÛ·/úÀüÅ–-[næïyàN›6m¥4ÔÓÓs–ð`(µ‰D„9f^¸»’:Ó_úDÇ¢³=?n토pGLb¬Þ*¥£9ÔüÇ …ŒP'„€KÓy«—X–Ô"`“PæT’‰œ€Ì]DÝ4¤Ó. $ ñàAøÓG7íB(‚šš¨¬¬„êêj¨¯¯‡E‹Á‚ `Þ¼y0wî\X°`Ìž=*++¥‘äRB)…¾¾>hoo‡––hkkƒÖÖVhii¦¦&èéé¡¡!„ÁÁ8¸Ô…p8 ‘p"‘T×Ô‚m‡ d[`Ùöh‹·ð=Íœ[/ý4ïÕ’oNܾ^b>û#"eþ!ŸjkkÓ^xáÏÃÄx¬X±b×i§6 ¥ŸÜLô›Î½óÎ;÷?õÔS-º•:úûûëׯ_sww÷ ÷ÝwßÈ?§lä.Çï~÷»ºÇ{lù¶mÛ.òukx×uíáááÉó“Ç'Áuüë©}ŸfÓ}Lú2Y)HmgÚ‘üÇܵuëÖ 8p¾I·ûöí»ðßøÆ7n|ó‹_üâ»wß}÷ÇÓ¦Mó–x-Ëo~ó›ØO~ò“O¿ùæ›+=ºÔqœ‚‹ÓÂápÿ—¾ô¥ŸoÙ²å¿Ê„;ÇqØ éL#î~~ÐùäQFâ½h»¢=€Â}œ8Ö×çN¯ªÙû—ìì>[&+gE¾KÝÜ <Ÿm¡—jFs¬fs-ÁM§ÁI§¡`ººº ñàAx÷ÝwÁ²,°m;oýôyóæÁÂ… aÉ’%°páB˜;w.TUUAEE”••e¿t"NÃÈÈ C?´¶¶À¡CM°ÿ~hjj‚#GŽ@ww7ôôô€ã8™ÕY²©A¡p"ᄘQ_™Ë)÷VðaÏSî“&{žˆ r.\9F¶–»à¦LB;`ÎùÔ_ßÜϸ§òq³fÍš?¿üòˇUi1³gÏž¥ÝÝݳEÑüÊÊÊ®d2Y)[“›Rj …`lpãë>J¸¤Û¢ömºbQö— ^ôÈ¿RéÇä"`ï»AâK&8Eû4‹Ñ¿ÿû¿~ݺu3:;;êúœùÖ[o]»k×®Õ?þxãܹs›çÎÛ^WW×W^^îô÷÷GŽ92£¹¹yþÑ£GOÇã³Òé´tÉ·3Ï<ó•»ï¾ûÃÍ›7AfC)µ²«[ù‰Ž ]qu&Â\61ЂÑvsP¸—J© £Ë*…|ðÁ¹Oýä_-þC]˜#À¤\'óY äÄ9g›—.¤œT*ánƒ÷Þ{?Å'™´›†úz˜¿`œzê©0þ|X°`Ìš5+—G_UUÑhl{4}Ôqœœ8ïë냶¶6h?rZ†Ã‡Ccc#´µµAOO¤Ói „€mÛYnA(‚h4 –Ûʤ¸°~"ÄÿªjIÎ{î9÷·»g#8ï¢;ª²ûü„æ°L".™]¹re÷§>õ©ü÷yóæí¿êª«ŽÂøœ꺮ÑÅ©¥|ÝÖ­[÷‡;v|¦¹¹Y»ÚK<mß¾ýŠ7ß|ó¢Y³f5MŸ>ýx8Néïï¯êêêšÙÛÛ;Ëqi Cmmí‘[o½õÅçž{îjY¤ŸRjŒŒ„alÇk¼$è…SàþuF¼Ž»*²ëa´Ô£é ¸¾”Ñ_…{ã7Ù¿ÿ³ßûÞ÷¾Çu÷ð ©Tªº½½ýÜöööswîÜÉöaež9sæ;ßÿþ÷_¨©©I…B¡”¦?oª:Ç¢sbU× Ó îc$1*€JæQ}Ž@¢]tÑì}úË¡~RMtâ\VžMxô ´öl´?“ãmMlˆ„#å£ÖÍæÑ»4sQlwo/ttvÂÛo¿ Žã€mÛP[[ Ó¦MƒX,Ó§O‡¹sç¢E‹ ¶¶6³ŽúÁƒpôèÑÜÅ¡===088™*…ÂagóÏ-+“Úb[£é-yË- 4?½ˆ9N‘ –‰káPEÑxOÑ6?‰¢i`jç¸Ã… Ç ¢¢‚\}õÕïïØ±ãÂd2Y¥³/++¼ì²ËÞÎ^”:^ï£~JymÂç>÷¹þûî»ïßî¿ÿþ¹###Fws§¼¥¥åŒ–––3üôU]]Ýñ·û·Ï\sÍ5-/½ôÒj•m___Æø:¸!wfU™¢ç¸Þ͸¯-1ÕM1ºk3!„fSed}™DÛó¶~øá?E£Ñ=öØc·uvvú]í%ox¦†Ó¦MÛóØcýàâ‹/îknn…B¡•ßD"á½Yq­Êmå·ƒ ÌDìƒÄui wC²Ý€*¨Œ0¯€dÄy2ö0d–=Êû©R⺂4S!n’‚À}ÈÄ©¢Nå›BæR¶eØ(ó&x@]œtÜtŽë€ÖÖVpB¡LÎ}(‚T*Éd\×…H¤ "‘p6½¥l+U÷î4J ÄÊ<{¹á¼˜&^‰"ú-Óâc‹XÃۜש‰Æˆ{!îÚµk›~øÃ6·´´h£É ‡n»í¶`tå‰Rc-QTÖ#½nݺßzë­_|ñÅS©TIn6‹ÅÚÖ­[÷ìúõë?:~ü¸Fã*ûx<^žL&!‰”&)0ú:=êíÓvÜ#îܧ‚Feµ©4¿~D:¿Ÿ·}ÿý÷7ÖÔÔüóC=ôß:;;ù‡oêëëÿðÀsíµ×vAV„Û¶íê„{öÂV~âÁ¯gÏn«Îì!²IRDNXážéÍC¯€Œ@¯€ŒH¯‚Q‘žk–}&Ì>»L™HÀ Ôé'•(ªËv˜—b!0ã׆—ùã}8’Øf_Õž?xv¥;‚P8”‹dÓ‚Qúc/ %…J:Ñ&‚6y~$õ…íEb›(ûæ·ù2‘].7žïuœòÂÝq"ºÅu]ö¦6'îœ9s:mÛN9Ž#îµµµGï½÷Þw¡p¢_Jh*•"ª ¥ÔN¥R¥ÝèOúÓmëׯïÚ¸qã•---gÕa,k¿à‚ ~ÿä“O¾vÊ)§$™UWW+oþÔÝÝ]700`•——›.?ÉCÇÕëN§Ku~©ëºÚÕ’É$+B‹Ù·÷÷.%N[àÿ³ÎÄqmÀÀq›É\2î|¿'x׬YstÕªUÿçë_ÿúÞ-[¶\ÞÝݽh¬«£Bh,Ûá…þÛÆ[UUÅ®ÕÕÕiBˆ*Ç’Éd%äŸã BÝ$˜`"âu“#Ä''„pÏþ!U0/z…Ñ<ô0òE9d-ÐÞ^WœÍ¨æu]×Í^ðI)5^yE–JC)'6}¤Ö˜¶ãŧ.o;¯œiËm2v’(µ"åDT&·Þ§dÜŠ4™<[ɘ =[gY@˜÷@ r\ÇX,6P]]}Dö~v'TSSÓ 'HŽ{²gÏžStë=¯\¹ò·+W®ìƒq~ýkjj†c±X["‘¨ÌæÿæA)%‘Hd¨ªª*ãôº}ë[ßÚ{Ë-·ø‡ø‡ó·oß¾¢½½}‰ÉÝN=lÛNÎ;÷à .¸`×í·ß¾ûòË/ïšN§Á¶íÜø/^ܺoß¾vÑšÑétÚ¶m;ÕÑÑjhhPF4UTTT¤êêê:(¥¶mÛ€t:mÇb±ã•••Þù-jŽ{]]]oUUÕ1˲\>m‡RJ\×µb±XJã^QQ‘¨­­=â8N™è½•N§íººº®P(”Оˆ®­­¬®®î „8¢ÏÇqB±X¬;‰8 ?ϲþeÑãQ___ŸúÅ/~±í7ÞøÃÓO?}æo¼±¼µµõÓÉdÒ× #‘HßÌ™3ÿxþùç¿q×]w½ùå—÷‚$ÿ|úôéÇ¢ÑhŒNús㢔†ÊËË»{zzH]]/ÔUk·‹î¨ªŠ¸‹&8ªÉXA9æ·cL3ÃÉ3Óe/õrÒ½ucÕ{T*‡‚ááaXvþ²_oyýõŸÂî(ß·o_u(~§R)kþüùCŸùÌg”) Ÿ ¬ßþö·±ë®»îôööΗUWWùå/ù¿.»ì².ß 9räHx÷îÝ5©TÊ}qRJI8v?ÿùÏ÷ÔÔÔ<ÁرcGõ¦M›Nþýïÿ©Ã‡Ÿ2888-•JU$“É0¥Ô²,+‡G"‘ÈP]]]ǼyóšW¬XñÑu×]×|î¹çä-+˜GSSStïÞ½Õ"ážJ¥¬ÚÚÚÔòåË{ËËËÿMÆãqûwÞ©íïï‰þ.Ç!±X,uþùç÷WVVýüîÝ»·òÀU¡PˆòÇH)%étÎ9çœþ“O>Yuc :;;Ão¿ývLt~2ÇÞÐÐX¶lYÀërmãÖ[nýN"‘¨…ByK#ð_ÞS0±njk÷!šÙaëÖ>ùÑ#H½lb‘«×ü Šô›œ'B$ 8t茌ŒÐÏ/ÿÜÿÛ¼yó/a w0ÿ 9Q> ­¯|å+ñÜsÏýµ*å’K.yþµ×^ûw6<Θ¼n:¶d2 ]]]ßH¾ IDATá={öT{k¢‡™g‘0¦Œêlkœ­JÈS® ¤¬¬,Gâ”Æg°Þó7÷áÕ¶y¹ Ê«°)°clMû`õ¸Ð¿'€=#_]¤:o<º±hë¡ ^—ÛÎGÛ…mEÑýÑÆÎ‚”ã@:íqgΜ٠S_¸# ßúÖ·.9vìØ•Íi§öî]wÝÕùŸ1‚ŒSnR§ ²è¼JЋ¢Ñ²2“‡*_ݤÜįllü1úúCÑ>6&T¸gsÒ dÄ8+Ô+`4=£ùèÞ¬0(çº?T^p›´ñìLÚäê¦M›–,F{»]wu-Zçgz”/ÓH$v²r~IIÑÁŠoÈkÃogl<ÿâHw¾,æÆî-)éC´ëÆ*·Ø'ÊãÖMØ¢4oƒR†tæ&UéSN9¥|è!“òíoû´wÞyç2PLä+**ޝ]»öwY›©œ"… ãû[ŒÏI™@—í›Ö‰¢î®¢Þh—Ù˜´UM4t~Eöº2Ós‡ŒqîÌ Œ,ÈêåYŽÑ†üµÑ½¼tï ^¤›þ!SÆ—Nسõ²m‘Ôçê¦M›–¨‰Å:ZÛÚ2weš°B‚\ f „¶²Üqv_æË$ï\–#¿Ù=娏²s}«¨Lzî8±.›a¤_QÇ¿†Þ±º® CÃÃ@ RV6pÞyçoŸÈ®]»*žx≓ÉdµÊð¬³Îúýƒ>¸ðuGX!éW¼«Ú˜èDv¼°•‰wQ™nÛoA¨ÈF'øuÈÆ+ª×ù@ŠDÉ„;s3#o—(dº÷ðr@EQs—«ãß(&k)²b_4kW n••­Ì=餓óæÌ>üá‡û€R\š†Ê5Uå•Ëöu¹íÒ‹E}ø±aíø¨sª¸ùÇ-ðiy'|Y¡O3?ùÇ!³®?’wŒ…ýyå–E •r >4”ºP«m9ãŒ3ø¼GdêA€~ík_»öرcŸRVVVùÆ7¾ñäßûANtDß~Ŷʯ¬IªHµ¬¡.²S‰kQ=êLÐEÜUþdçC¤Ó„`šÌØ)špÏ^@‚ÑtvùÅä u ™//ïF l¸ÓÛv™}ÖŽpul¹·ÏGØùp*uEáUÑv‘8gíEXçw^ãÖ­[Gœt:šN¥!l3©ú4+ø²­GÓ[x¡H Ûä "u¶ ›Ò’ëƒ0íˆÄ·ÄF%ºórãÎGt\D0¯^7V"ð-*3éŒáÛ*ÒiDýBÀ"  @b8Ô¥pÊÉ'ÿ©¾¾>(ܧ:äú믿ä½÷Þ»RuA*!ĹôÒK_Z³fÍ1Àh;‚ˆËg¡Jœû›DÜE¾uÂ\¶íí‹–TT=D9î )gëÛ× u“s(š˜ %"°pg„º·ÚKd³Í<³B˜}¶œ½‹À¨ð&\™(ê-ÕÀõ§²+8,I¾½*B ëÇ=ÿüóTTTvôõõÍwÝ´ðFLºˆ°jÛÛ7i«ó!²QGáÙù‘¿~MóÛƒ´5õÃþ™ú˯ïï×u! %Ï>ûìýÙ¥ñCn sÇw,ÿÿø[t7[š3gÎ7lØð&àÅÈ"¢T~¢í²r•Xgëe‘l•pæŸM¢¶ª¼zѸDÇ¢«3ó<0Ú^$Œ„;s©—úâ vÙZéÞ›’@~ô;Ï-ŒŠw>ÒͶ÷lMÄ·*z.òÁNt‚Ýø÷ÈëëÒK/íœ^?£±·¯o¾ëRH§Ó`1ë¹—:O]—³.òß ê3Åšþt}™¦Æ¶)N*LýèÒsdǧšœ3°lF†‡¡¯¿\J¡®¶¶é¢‹.jÿ~Èä€ý«¿ú«Ï?ÿüóH$b*ãP(4tà 7ü{öæ@øz#È(üwt1|ÅÎO´]U&Ì¢6"1=ñ®³5™ˆ¨ÎƒÝD¨¯/’E(Ü¡N`4ÍÅìl]® ˆE0påÀ•c+zQYÁm‚îÍ!«WµS vS1ŸëçœOŸó‡æ¦¦Ï§tÔqGÂL »‚ ÍÌ£]æ§@~[’oS'†<›üôÆ–‡8Í…µåÛ@ _O˜ ß–°õÜ8%mXÙ•jDç‰ú󎋎ˆî˯¥ïÙ Î9°ÞÞ^ æÏ›÷ÁòåË»s§"¸×\sÍ^}õÕ›‰DʘBO=õÔmßùÎwvg‹ðË A )Öß…ÎN`ÊÊdúEöìm«Ä¾N4«=¿/Š‚û¯ãdmùñªöóÊewÔEü‘îÙÔ/EÅ»+©·ï=Lfª4ÛŽÍC'PxA)û&aë)³ÏþìC8{")Í6ûÌ׳ý°vü>‹J¸óuîßüÍ×wncÛÁÎãg¦R)ˆ8‡GsÝ…Ñ\FPò6º\nv¿ _m* aNª.Ò'ŠýEÉ•ã{áD@_¯Í‹LPôçÉ‚D" GŽ´“†²²HüÒKW¿UWWçæ:O5ȶmÛªî»ï¾5»wïþK×uùûIÐÐаçG?úÑO³·eÇ×AJK©„;¿¯‹¸ËžME8€úî¤Þç k«Š°ë¢qÊ|ÉŽ'BÙèzFÅ2«´Ø„åáE«è@ԗמßA4¾Îo^E—ùòe¿lÙ²OŸ}öößmÛ¶$í¦íd* áH8¿fŶ©ÂÔ¹/_¡€e› ®Üwc“[UoÒŸ.=I˜b”Ý´m Žwv@|(.u¡®®î£»îºË[?ð&? “ 6|úg?ûÙÚãÇ:èPFõ«_}fõêÕ½€¢AŠMÏO?Â]T.‹j‹¶ƒ wQ?‘uÙDmE>Šõ½T0.Œ¶/âîEU‘e•Ðæ÷E/¾Lè{Ûgk*¸ù¶º1ËŽ u² ƒÈ‡±Ï{î¹ç÷ï¾»ë²þþþSRÉ8"‘ükÛDé)~·µ¶T&¸¾õÂ2ö”â:ÿ´¦í˜õ'jkZoÜ•«à<3Û¶!‘HÀ±£Ç í¤! 'V®\ùúœ9sR€Bn²ã}&º?þøÉ?þñÿ²±±q•ã8U&mÛ¾âŠ+þõ‘Gù°´ÃD?BPe+½*UäY'ØEeºH9_&ÚµS uU½È^´-³aÑé9Ä'£ ‰«Å([/¤|½Hà«„½ŸVdË^Èj:&ÑD…-W ~Ù¤@<Ž<®¼òÊãçœsÎkoüç÷¤Ý4ŒŒŒd„;aD Hn[d£µ•øå#˼r>oœK—Q]´É ö¼|ðَܸ ÆÊÕ³eÞøu~D©1Fç”í#7v¶ždχÇÃàà ̘QÿÁƒnx 0·}Ò³sçÎÊM›6Ííµ×>ûÑG­ŠÇãsLÛBœÏ~ö³?þùç·–rŒò ¡˜¢ÎÔWÐÈ».Ú$êÎnÝçSeDã7©ÙóÇ(k#³CŠ {q*/fÙr‹)7ÞlŽ;0mçïƒ}fÇ%zx>dëÁS-_PxlüñˆöÁ Œ'góÈ#¿~ÓM7×ÞÞ~A"9CÃ!¨ª¬5ÌÁêå_@ÉÖƒ ¾0:,¬ÒFÓ^;¹Øgv@9ž‚6>Æ/ò#<§9A.™ ä¥ÈØÐÓÛÍMMvÓPVVÖwóÍ7=»dɩÀl“rï½÷^õÇ?þqM*•ªñÕgéÒ¥/ìØ±ã9."RH)þNüF’uå~Å»êY%êE"[VçÇFg+*×#¿Œ!B¥”òeþ…‘‰T^ ³6¬ –µ#PøR‹ïSÛ¿h¬²r"/²!Ë—/øò—¿üó'Ÿüñ§’©dõðÐD"(++JižÏ°ñ;†ú@§ |ê£ÿÙza=!ò%Å¢~lçDÖ_P!L¥àãƒaxx€,Y²ä•õë×ïL‘™ XõõõºµÙ YVò¼óÎûÙ¯~õ«ç 󷎯5‚L*]à§Nu={Û²}p—•«|èìùq¨&'ªr?ítuHøå YAËG¥yD6"q.‹jëET¯kÏ— \Ô†ŸD°íT"]w|^ûè£î~õÕ×_ûè£?]ç8i2Ð?ái!…ÂñžsH ·)#™maþº(MbUÐ*O>XÞ8c¯˜(è„4HŽÉï±(s÷©¤^ô@þñúøtww¥Nš9s×£ßùÎ Ñh/H¸W]uÕÛ·o?ǘ4ˆD"+V¬xvëÖ­/—zp‚äúyj"Pev:±®²m{û~nÈ$óË߀ 8™çíE>Tðº¿çJ{Q§É‹éç d:ô;ƒ-{¤z¸ÌCæG´-j'ó#²= Æ÷È#=7cÆô÷(¥L&a`` {GÕL.¸÷œyî‘©µ!v…u£>(ç¸>ù¾ó Š2ú›ÅŒÑâÊ,È?K äólÿ¢íÂ2’7.‹ˆÆï¯Þ²,…lhiiÖÖp] ee‘Þo¼áÿ^tÑE}Þ»ø˜˜‡{ÓM7µOŸ>ýÏ`@MMÍ[n¹åÑ­[·nšcÇ>¦âƒýŽT}‡j¿S>uíLü™ÚðÛü³ªpå Øn›÷%²c÷EíUýˆÚˆöem²Q6‚+Ê o9H êH5»ÍÖS® ðh4oËæ­{oß›U²uì˜øüxÑqðÇÂŽ‹÷Ç׉lùãEõ ŽýÚk¯=ÞÙÙùÝ|ðöõõÇ ®®,+¡O»ª/úTG­umdí3Û£ö:þûS·õ•:cP/;fYõ¶7q± µµ öïߎ“‚P(4tñÅ?ùè£ßÛ“5Ç®)Bmm-œ~úéï·´´\L)Þ¨.÷œ}öÙ›6lذéšk®éLA± ÓÅìǤLT®«¼H‰cþÙT°Ëêek½‹ÊTõ<²ãôûÚàw^ ~!1°H*!*Á:Á®úe}ÈD³ªŽ_e†g'š€ð~d“Ñd@d'¢`Rt÷w´ïß¿ÿ?õ“ŸüÁ“ãq ‹Å ΤÍÉS®^zQeÖÖ5i¤¿ yåʱHÆ`*Ðue–eëºÐ|ø46ÇIeÛβeËžÞ´iÓf@¦"îW\±{Û¶m=©Tªž­°m{pöìÙ;Ö®]û↠>ª®®–}ù!"F¦ ŠõwdêGe§›.²m*ØEe*ñí•óÓ‰€èXT>T¿^x×ÔâA²÷F™EÑlÑë³4vAÚûÙç·Ùcá×yçU´Í‹}Ó2SȺuë–>ýôÓÏ´, ¢Ñ(LŸ>ÊÊÊòY{Ù¶Ê;o[7~‘å¸teõ¹}ϼmÈvÛ¶add„ææfH§Ó`[ÖÈÒ¥K7nß¾}cyy9~PMQ¬E‹}÷øñãË F;êëëÿ¸jÕª-?øÁvÕ××;=F9Á VØtBžŸp˜D¦UâY%æùœwQtÝçëÙ}Õ]Xe¾eõ²1¨ž0]¦H°ÂÀ\´äße5¨0çý©úA=¶ƒ<ƒá¾©IÐ~íÚµ+6¿úê½ñx|>@4…Xm-TUW!$wѪ0 œÙÉ/SD‹Y{‘­È_þòüeÛ˜úÓö§ic46Õ˜ø²ìäÉË»ïëëƒ?ïßÇŽË^›`œ{î¹Oÿê•W~^__ëµOqV¯^}åûï¿ñÉ'Ÿüþ…^¸ëþûïo̾®ø„ ÅC/E?~êüFÝùm•PçŸuÂ]$¦UBÚ±/Õƒm+ó+›ê¸Q´ÍáÔ‰a•°ö#ÄuÂ_ÕFe/{‰t‘ø6ä2;*¨g·ù™¾ òío{áã?úÑïìêº Ù­¬¬„X,g”ˆK‘P¦ªzAÔY&®óê} e¥0fÆDÌ«Žß­î\åìH推CCCÐÜÜ ÍÍÍ022„ˆF£í«V®üç_mÞüZ¶)~PMq:::B###dþüùºœ œRwÖÿ]ÌoËúÖ•‰„¼LÜ«"îì¾LÌ›î›s?íuc‘KÁ6Š÷âÀ w¯L'–u¢@q„¾j_46ð±¯Ú6í*[ÓöB»_ÜT÷Àß¼­©éЗÒét€P8 555‹Å ‰ä.^Í[:R”"x–•©ÚË·i™Î·Ÿú ¶Þ/–eåÎu2™„¶¶68|ø0 @:˲ܺºiïÝxãÚ?þøã{AñC©…{Ð6~¢ðºr“g?x]êŒßˆº‰°õ§:¦¼mîÅA&ÜÌÒStÂ[%úMĹÉd4u2{Þ†·õ¶½èö[¯À÷÷÷[×\sÍ•ï¾ûîÍCÃçxQöH8 µµµPUUåååyË%ÊD¼2"n(nƒâb—ew2/ˆ¨žIgaËò^Tƒã`Åz:†øÐt?ííí¹õÙ)¥‡{/^üÒ÷¿ÿýŸ_vÙe}€ ‚L$¢ïV`TիļnÛ¤L%ÖƒFÝä©2|H¨ËÒtDýðõÂcEÑ^H´-3ñò…³Wç'¯‹Èë"ð¼O¿kÉ£pŸ@XážÛ¹0 o~Ÿ·W }Ó‰n,¢gÞŽ¯“µùà "ÒEeº72;>ºaÆEÏ?ÿüU---_H$'yÂÜ[Ý$Cyy9”••A$H$¡P(A6M?ñöUâZäÇïD@æ;ˆp× {I?®ë‚ëºà¤R0<2###Ç¡¿¿††† ™L‚ãŒ.bYÖÈÌ™3·®X±âÕo~ó›ï/[¶lpýnARâGôñß­A£Id]d/›èÒITv~¢ñ¦‚ÝᆰoÑñä•¡p/žp·AœܳH”—rßÏX@`cR&Ú— ó "Þ¯ÈWú:~ü¸õì³Ïžôì³Ï^xèСUñxüôt:]ÉÞ$ÆÝ죔Êïx÷諒PJsBž­¶m{(‰khhxëâ‹/þÍý÷ßÿáé§Ÿžòê‹2A¤øùŒ6‰º‹êtb]TæW¸›lãì—žh_Ýçë³Cá^dwo[ÙÖ¥ºø‰Èƒm¾LÔ—èXLëARnª‹7âã?ŽüÝßýÝÒ>øàÌŽŽŽ3†††Nv'æ8N•ëºQÙ)Ô¶í˲†lÛî///?RWW÷çÅ‹ïùò—¿üÁwÞy|¢ˆ ‚ " MÚ˜ˆye´YQ®K=Q¥£ðé6~/^•Õ‰|ÉìEãµïÅ‚¸‹"צ‘nÐ {¿ Ϻ2ѾŸh»ªÜ„b„Œ d#ñÛ·o¯Þµk×´¶¶¶ÚžžžÊ¡¡¡òT*ʾ¾ˆBˆ …Ò‘H$‹Åâ K–,é^µjUßYg•€ÑŸ^ñCAdòô³Z—£*²­‹ÈûIQQ]PêçbS?i2¢}ÑØ…{±à…;@¡xæËDuAÄ|PŸ&Ï~ÊDûªr•Ø.vîˆ&ljÈ1úàAAÆ £“RöaZ$uFiçëuÂÞo.bîW°›ŒSÔP¸ ]*ûGË@¶Lµ ÿ‡'Ú–Õ³ðåª?f¶N4vÖÖëCV'«÷Ì,™?G2¿ø ‚|Ò`¿‹¡nb#j£î¢rÙ³·­ò&ý˜ü BVïë5¡”ïcÇ$Z$Êu¢ÝD¼‹ìEÂWõ†QM&X?¼ˆW‰oÞŸÌN'öuoNS!®úcFAO:¦ß{ňÐö&B]dg™×EÛeö¦“]™l¿Ô¿† DÂ]Ý• u€B‘Ì—±¹Âl Œ—«‰hÝD‚µWM$x¿¢ êMĹ â‚ R\tÂÛDlú­u×m넽©¨— x]{L”™Œ¶V¸ë"Ø|½ßè»LxûÁ4F„,Ú¯²åûÕû‰ºcÎ9‚ ‚”“èx1Úðõ*Qob'Ùº_áM"æºr•M`# õ K•‘‰tÕ>€\ÈËf¹¢È>ßÞ/¦¹ï²¾d¤úù(E:Š~AäD¥"p¬"Ô$òΖëD½Jä›îö¥8¿yÚ sÜ‹ƒßu¾UQj™­.²­ºèT–’Ã×ÉÆ¨»èÕô‚W•¨Ø UAäD¡TB¯Xé A½itÜÄÎä?Ñòb—P´^¸ëÒeØ}àõ{Ñ‚¬oÖ—(ÍE&ôAb£³ Sá\LÍ_€ ‚ ' ¢ `1àx w¾Ì϶*ZoM×EóA)%(àÇŠŸˆ{Q/» T–û.Ò*ÑíùQõËd¢a2)懊G©ÊBAÉLQ„£ÿ õ"?û*q.+7í¦ãQÔó‚½8¨V•Ñ•ÉêeÛì¾Làˆß(*ï•«úUWUæÙØ1ZŽ ‚ G)£ô&¢ØD¸óûªh¼iTŸ·Õõå÷<ÛcŽ{qšã®ɲ;Hû‰–³ÑvUSá,šHðu²z•O¿iC‚ ‚ ßÁc‰ÄÀñ¢2•°7ë%Ñ,(ÞÇŽJ¸ËÄ®L¼{¨D´jU]Û^µÂ¨Óœ|¾?AD8FÛAdâ*MÛ5êÎïÈû­3í[5dÑEÜMS`tmDbŸ·—µ0› ˆ„·lb¡zÓªD¶_Ñ^ŠœwAA‚SÌt Qy?i.:•½ ²è|P¿¨wJŒé:î²ra/k#Šxó‘u•æûQ tѱ™ˆlÕ˜Líe`TAA‚Q,ÁXÌ”š ©4A¢á²ö¦i6AA‘>xÂÝ4‚®«W ~U„\U¯»˜Õ$ Çd•LÓjü ;Ï‚ ‚ä# Ëw1lÇš6£Û7Mg1íc‰Þ#ã€éÅ©¼8g1Y_ÕÏR‹ª4“œz•¨– c¿åÅ~CcJ ‚ ‚ø§”Â]էߺ ÑwY™It^%ÒýDâMƆŒ#A.N•ÙùI£‘áGÀÓÖä—ÞW±Áˆ;‚ ‚ø§ÔQb¿AmL…¾i4ݯ`Wµñ;Ù˜"B(¥è£Îª¼pS;Yꊮί­¬?mLý Y*‚ ‚ rJuŸè¼wU[¿bÜÔÖO_¦¾ðFLc„¸ûÉeW!KYÑ­þ¢J‘ÙÊ-'ÉÖÉi/õ›m"~òCA©Šß(p±ú«}HûXÊMü›ˆv?¾‘ÀFÜóÊd¶ Y½[S›REØÇ3ÒQuA) ã-"ý f“v&:ˆMÐ4?6ÊrŒº'¨p}?vA„¼ŸúbµAAdòSÌèúXm‚Fáýú2ñ;Ö‹lQ´‘b wQ¹ßȺ;¿¶*‚Špï‚ òɤâÝ´ßÕ\üŠö “b¤þŠ÷± î¹rU[?é2~ËLëƒÖ•ü‚ RZ&RM ÚÖpöe×ÉwN< ÷àø½sªWL½l%Õ‹"ºSvQæXVZ‘!È ÅÛø†EA©E1#êAÚ•:Õf,ý"ã„q'à?Ú]¬›•êBÒb mŒ#‚ ȉM±ìX¢ò:¿¼”…0âÝSýDÞMËù:Q¹(/ë_„iDÝTã AAJ_ê%"ƒ¶AÆ]Ä=ÏÎÄW‘Ú™´K|¼"è©GAÉÇd[ó=ˆbçÉ›¶S{Œ¶ þLêè¶îMÅŠœ«Pý c¼n¤äoNAA&jyI{?~0?ÁˆRe‚¤ÇÈìx[!/³µ3}ƒŒ‡`Çè:‚ ‚L]ÆCtNô®A}¢`Ÿ$YUÆ« o+²½Ð~}ÊPMŠMUjA™<Œ÷÷x1û+E„¾”~‘€°9îJS_AÇPBûR1YÆ ‚ Hñ™L‚´Ôè›Ö ¶IDAT¼ië»Ìo;&Â]Ú®„öãío²÷‹ ‚ ÈÄ1Q‚³TýŽ[ÊŠõâT¸ ýL@ÛR3™Ç† ‚ ÈÄ1™éXÇV”cCÑ^|r´â=Ïß$ó… ‚ òI¤Xâ¸h"{éŠã" ù@}O“i,‚ ‚Lm&“p-éXP¤/ë8ûb2•ÆŠ ‚ È'—)%tQ˜O.¦Œ bAA¢ƒBúÄæÿpˆ®k@IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/transparent.gif0000664000175000017500000000006112203711415025610 0ustar takakitakakiGIF89a‘ÿÿÿÿÿÿ!ù,T;pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/favicon.ico0000664000175000017500000000257612203711415024716 0ustar takakitakakih( " A>;)'%+¯ô=¿ö420520ÿÿÿ;96¸èú „„†µäô—àúGDA/-+LHF+©é\XUtØùååæììì,BO/Vi531WSP+®ñ·çø§§©OÏù--..-.QNK       #!!!!!!"   $$$$$$$$$$$$$$$$À€€Àpyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/pyramid-small.png0000664000175000017500000001560412203711415026052 0ustar takakitakaki‰PNG  IHDRÜ27µsKIDATxœí{˜Õ÷?çTU_æ>0ÃÜp„ŠD(ÞˆšhVØ,."»hbÜUß}³kÖ˜¼‰˜ˆ»„}ˆó¬1’Å Äý0sÙ™°íÛ„ilÛÎe·Yá8yIkkûÅÀÚS=ž!|ú‘3ÂM™2%lãLU6(ÇùDm9®p”"³œÌ(¶Efa.¥’6\Åá†äŒp0,d;#lÁ'òH:J H CÒÞÇ02Iôd]ZŸÂWì(gì€4„Ó¥À?Åèé¿ùŽ]Ü|›àù\ ,g„Êr‚*'¢N*<òxéîŽs×-—pVþFîÌA ÙCx)ï<Òy'2 :GåŸÀ ü°€¿ÆU@!tÞlÞ~ ´°3 pp!ð$ðǶ栟‡"•pïsÀ€CÀ†ösÂÈ¡ ׅ턲xiDF¡®&¨ô]ëìŽ3ãÒÑÌ›y>Öá5Ì»Zò‹W È )"y~z[‚äQá#¢Ð»Ž°Š[ü¨è£^#ð°| !¸ øw7ÿ9àR kí(÷¼B šv¬ 8þŒ–rÛ2Ø §NÇq~#|*±Ÿ¬ãQ)w¨«-áþ;'aÒI¼[ð÷×·°iW˜?n1´A'ÈàyÑÿ ýÜîG?«œyñrJ8”Êê¼H?iDâ´PÐäŸîºˆª2I¬³ åHò’n>ÎKÊ8Ö¦!ídÜ Met‘èã$`#°8­l!pšdÃÝýÕÀ®“Õé§ «€ï¢Uô:©Ÿãî6]ú :d®:êêòø¦'¿”)$†ÔI ‰”Ú¢“‘ØÚÜù7ã˜òÙâm º@YÄ¢ÔU:üãM],Ó41M Ë2±,ËMX@ ‘‚ Ë:Yk8³-"ø/à{n¾˜y2:û”¢X€¶±Þ8µCäÔ†ð—x:ãæ}RÈ'á¢QÅ5Ó«¸ùújâ](倊!”ä‹J®šàÐôyÁò5‚Ád[Bÿç 3‘Ü÷õ§ð ÃýÀKh½8Ï-@pÜÔ—Zc‘T±¼7³ ù ã$£$Unj"Ú©BK\´ûúè[ºý¨´~*j´$Ú Ä2œ[áÖ‰¢m¥Þl2ÿõô瞘À(´—±­9Ä8VUdAÎ$œ‡„ä1MÌDÞ hi¤%Q€` €”cÎ.æ¾[ë0T åDAÅ@Ùhs( *;–Ï-W+.9/€4B„BAB¡¡Pˆp(D(&‡ ‡CÉ”&‘ƒçÓt¸yÏN OëÑ^̪^Ο¼ãÖ½ßW~6Úíý.0ÈC«µ€ßS|uÇ»åÝ:ÞCKáK{éÿónß«Ð/:àhßïÝck/øÎ©E«…ï»ã[öØÞÜK?ån ©d‚Üâö¹ÁmÿOÀ:·}ÅiJºœI¸®®.By…zU—I)¤“ôåµ$Ï’üÃWë(+5ˆEõ‹U @Ù(Bèû.‰c0wÏ<ô\˜Ö0 é“jÚn“R‚ÀuܤØö K7€2ôD8èÝ´£åFwÿ:àçYΟ&Àƒ¾r‰&cMØ»ù¾ã–»À"·ØY£¿®D»Ò_Îи--§cÐ/‘(Žo½à¶µxh@K¿n·Î…À³èøØO3ôp¯³íAÌ„°{î-¾²f÷?ç¶ÿS´$>íCÂ4$á~ÁûÉ%¤&‡ô•Åm˜?³Œ có‰wÛ>w•a¢dGJÀ ¦$5e‚¯]àg¯0M‘ôˆ¦lÜr¡ çÄAõ7héZ"xx¸=碥Fú€ÂèI ðZzˆ¢'tM´Ùh/>FK2ÐoûçÑRu'šð!’®ø à_ÑRèXÚÚÝ­‰žÌÀ{€•nÙW€oºc]„&b-ðÀ¢oý_ÿÏísðŠ;?Iu9a~H’loßGKi&ôCÀh•ô´CNm8SJòóóQ(—\®Äq&š|‚X¦_Ϭ«†aÇHPàDAuC¼:÷ãÄÚô>1Šna0¹\°iT9O¯-¦¤P«–yyyäå… ‡B˜–…ÿc×h,“Ù1 db®DOÆ{Ýý?¯ùŽo~̦¢Õ¾ô7û…Àùnþy’“’vŽ7¡ß¾Œ~ã§ã?2”Å€_£¥ãOÑiÚæôóÛÂh"ÍJ«ó-´:øUà³nÙͤÆ¢_,¢íÇ™À’ cê £ÉZ5þpÔwü5´:º ¸èÛÎ rªR†AAA>Ji/¥!µÄѤ8JRQä֙ŧ 'v køa°£œã`·c·´ â¸î}´àÐmÀ_6ÀËkjùŸwBäA¡`‚‚JKK1b•” î滑Æ¡WIA«rõÀ ´ªÚ9qI—´‡¥è FOÂtÂ} ­;wÐsù‘ßV‰¡%J&²õ…U$%ÞzÎoý:ÃqÐÒùw¬o+2ÔY†–Œ…èɉâ«hõÖF‡Žf¨sMîœ,Õ:Qä”p¦iR\\Œã(M0‰K:‰eš!‰Åº˜÷ùc«Dìû (×ß ´ú®ï$†–!.Ù”Û†°?øë=ÌyìZ"6í´··ÓÜÜLãæF‚ eeeÔÔTŸ,)7½) -ÅîG;ÒñÚ›Xü5ÚéáÅŸBhÂv4fhÛǤª«½¡ý‚¨GK›*’o²>Îým–òÝè—I)Z•Íä´øØ&\_«sÒ"éØÙ‚vdûîXŠz©sJ[ÂY&%%%8¶:îf¹*^KK ï°™[¯ÙM}É¢‘ˆJmL9¨x’pB¸«T\òuÇ¡®|y;w.«G!É€mÛ477³¿¹™x|À„óOª½h±­‚BÛ\¯oõÒÆqô*”ïç‡B¡©kQTT49‰Œ(**ZÞÚÚ I 1}úôð[o½%”R˜¦¹?‹¹7,#„eYÅãñ»Ð^njĒRJÛ¶¥Âoù¯³=ý]$í®löW”¤·6Û8³aI’~HïAñn7vÈéJ+`QZZ‚m;˜¦‰aìÙ³‡õë×óç¶ñÅ©qæ^! €ö8_…a$q¤ vâ K!=Ò è´áÚ±ðw—·ð“Õ•„ƒ*ÕQC2‹FûíBVJy±"ãöÛo/xê©§„ã8äåå½ßÞÞþ÷ÍÍÍp8l{±+ íy3}Ép“ ˜Ë–-;úµ¯}­;‹îCO,9~üøÙo¿ý¶QPPptݺu…hÇJâJ}ôÑ’3f„ººº¨«««B;g2EΜ9órÇqn¡P¨«®®îƒúúú=ÕÕÕÇÚÛÛ+V¬¸!‹'NœØÌQJż¶¾ûÝï6,X°€ë®»nüÊ•+g ÉG«²öO~ò“a÷ÜsiÛ6åååE---gùöOÜ[Ì›ÛòÉ ø–u¹a˜+ ”*GÛAôà ¦% ýÀ-/Ýwß}…¿üå/ó#‘£F Ì®¬¬ôŒÂô‡îßO™LóçÏ<òÈ–ÆÆÆñ;wîœÚÔÔô?Ѧ¦¦K.¸à‚??ÞB«bÞùÂ4Ͱ·HÀ0 m¦;oŒGyäÜ—^zižã8Œ3æ£'Ÿ|rÅå—_ÞŠûzÚ¼y³õ«_ýêºX, ‡Ã^ˆÁ›üƈ#ª½ÆjkkkÐö—w\râĉE–eY¶msî¹çŽF{L=Ò*Àž3g<ð@éáÇ©ªª*ß·oßL·N7ÐýôÓO}å+_1lÛ¦¸¸¸àرcUî±ØÅ_l¾ûî»Þµy÷!NK%ä˜p¡`ˆa¥ÃXµj+_y…ÖcdžI~ždñ½6UÝЭ0Œ6Ž·Ei9;÷±«¥‚ãñQXã(¯¾€Q]€yI€[7òûèÞûGÂ]pV~+£J5 ó `€aÀÿr7óžj ù¸‰e$%›”Û¶©®ªªFÇÃ|2²TÚ˲‚ÂñBÏ18ƒ9kÖ¬w.\8þøñãÅK—.]__ôСC#¤”ö¼yóÞuÛõ·-ðM,¥Å†M†É¶lÙ²KÇ!G–.]úÜ´iÓ"$ #Ž;–ˆþ;Žã¸Ç„r'!ylý¹~”Ti$mÛöëåÞêïå#Ca†!B¡P½J$ÑFCCC¡GÚººº³Ð×(`¯X±Âœ0aB<‰PPP0¹±±qFMMM­¢v¢UÚ. »¾¾~øÖ­[½0ÌiÏ)á"‘O>ù$o®[ç®›4±Å?ÞjpNíqÞú}ˆÍE4·Uc„ÏeDÍxªÎ=ŸË¯:›êš* óSÕþ‹'}ø[:£°¿ùû÷î¢i÷G¼½kÑ)ÛY¸›1åðÀuøÖ‹µØŽp=¤Ú;ª”ðTR…ž$ý ˜ Û¶mw¢cÛ¶·ìi oWçî»ïþð‰'žh>räHåo~ó›q›6m:¨”ÕÕÕ»n¿ýömô\=!DZ=õ, áľ}ûÌŒ6lXË´iÓ§µc´´´Xñx<‰G ¹m½:@@‚éäWJ©LäOyùH)½c¸õ£¾±Û¶ƒi×ãõkÔÕÕ‰ššš=åuË—/¿æ›ßüfIò'^cÆŒ¹rëÖ­ùeeeE<MÌn·Ž§æ:îõæ 9#\Ie%ï½ÿñxÓÔÝÚŽƒ)b<óëc¬Þ0‰Ë®šÅ¤K§ñűc>¬ ßm‡pÎÈ ÎYS?ÜJ¤#ÆÖ­ÛøÓ{ï²|í*>n\K@Dé6ò0¤V-…”H¥PN<}` ”R‰ Õ›„骫«™4iÒV­Zõ¥ÆÆÆqÛ·oï˜:uêz˲¼ñeퟤtM!\ ˆI)€¶¶¶Â––9bÄïÅ gÑ¢E—z„s_)dqI¦Ü>3Îñ% )QJ ¡p'ç¶íÒ»f¯Ž1sæÌ7/rG.^¼xæœ9sÖÖÖúíDV¬X1bíÚµó¼>ƒÁ`:Fè‘-%¹¶j‚€ 2 sF¸cÍÇœ²ò"ýC¬Ž“X¼W»óÙ¾¿‰ ü˜Â¢§(/+㬳΢¡¡††FMMM eeeX–•Ò®mÛ=z„ææìرƒmÛ¶±uë6öíû˜Ã‡ÐÖÞN4'f—0%–¡5Fá~­ ¤ÂQŽ7ÉNáuûí·¿³f͚Ϸµµ•ƒÁ¶¯ýëëIÚBéý'ÊôïÙYY™ª««k:|øpukkkùœ9s¾´lÙ²ÿ.//·×¯__ðï|çêµk×^#„PJ)F ’݃\÷¤]õ6‹´MW¯ÝωS…[&üª+É ¾÷\â .üà /¼°¾©©iòþýûÏ4iÒ?Í;÷…/|á ÛºººÔsÏ=7îå—_¾9‹ ·E"‘Ññx\’$˜§*§ýâñ4¡”b°H7(„Súï„Ð+ Š€â+VŒ¹çÞÿkÚ¶üM€’pPûö£±(‡¦å@ lü3+W¾Œ”’P(Dqq1UU•ÔÖÖRWW‡eYìÚ½›÷îåÀ´¶§;ÚR`¦e%<¡†!±,3é™ô–’I‰£Ç„sǶmÛ°m[2p•€Ù³güàƒnܼyód€úúú÷/»ì²#dVsE<·Çé«çÁ|iÞ¼yãÛÛÛ‡½ñÆ_<ï¼ó&‡Ãá¶£GVÄb±ð¤I“VïÙ³ç쌎D"y¤®»»;q_âñ8î1ÿ˜„mÛŽ7–X,&HµqÇ(¼ûå!eâ»m˜nÞõxu ž}öÙŸÍš5+oß¾}çµ´´4,Y²ä[?þx»ã8†mÛ!!Dlîܹß[·nÝ‘Hdt4õIŽo›ž÷ۜ괖pJ© z`¡›ŠÜF{÷ À>|øp@H©?PH,,Ö;©.{76—táC{G[š¶Ò¸y Ê•Ò00Mýí›aš¾u’Þâe\{M¦Í#œr{`„ …BÝ•••[#‘HiUUÕ>!᫦¦fÏæÍ›'K)ã·ÜrËëøÔ¥ôþóòòºªªª¶tvvTWWïÎÖÿÌ™3÷?öØc>üðÃ7îÝ»÷3‘H¤¬½½½xøðữ¾úêÕÏ>ûìÚ+®¸b¶ã8”””"Õ¶r†1bD ÊËË»ÇS$\QQQguuõ–ŽŽŽ‚ÊÊÊýn”±H)©ªªÚnšfgUU•÷)M¢üüüÎÊÊÊ-]]]ù•••“êåT€š|ôèÑ‘#GŽÜ°k×®GûÓ ô/õÚk¯ kjj*ª¨¨è¸æškƒv>Šh4*ÂápÒzýd:–^Ç0 2Ž¥:*JÛ¶q¯ÇëK‘Û·o7V­Z5¼µµÕ;vì¡n¸á š@ÑX,fwtt8¦iÆòóóÝ·ÄHÆýêeBuM øŸtd•p®Zh‘T Ð’,ä–ëoc4¡l´Qêíû€¨­­mµLëx4Ë—=ˆÖ“@é_ϳ¨õP„Éã25ï'\*Ù¼2½oÑh„â’â ýœè‚‰¬0nºé¦/=zt¤+Ý^ìϘN´ÿk¯½¶åÚk¯=HÚä5 ‚x?Ÿ ŽÊp<= ã'œ¨sÎ9ǹãŽ;"$½Ëžªk[–/..N'U6©Ö#ä3XHN)å-T  UAO%´H®ðts…[!± *‘2®¡¡Á.--iÚßÜ\•bOùåÿb@¥ÌGL™~®¿<-Ÿ$o²=Ó09~¼•˦Oû€Ìމ\@òú믿výúõÐÐа桇úà'×È6É{#\‚tô´ËÒ¥V62§ª¤Ó%šG*oëý>‡§§§_¤G8oëOôüxyãÊ+¯|mÙ²§§ç…ót°8Eí“Y÷¥ï«Dfòô8?퓟Ôvô:ή®Nº»ºwÝ{ï½ïqŠÖÞ½øâ‹åßþö·oÚ´iÓu€(,,ÜõøãÿœT;êLB5“ž¤ó“ÆŸÒ‰–t§DºARjy¤ð_eûŽ¥í MJ?áü«4R¤››â?þñ÷ÒÊ•oµ·µMËÏ×q¶LÒÍS“v˜ÌRž‰¬ú‡aµÓD¦J7Oõt¸hçÎL™2uY]]]¹—&béÒ¥UwÝu×â®®®r€¼¼¼= ,øþ•W^yøŒçtA&ÂyÛLR.pžzÙ›'ÒOºœBø$œ§z‹j yʤJú =É'Ÿyæ™ò{î¹gq8™Ÿ_€B%m-™Ý.“=ˆ—J ÏÉ"S—FL©LISÓ€6mÚ´„S#IÄ®]»¬qãÆý¼³³³fäÈ‘¯/Z´èg³gÏ>“É'N¸tâe’nqúV7=$I™$ÉÖŸ”N¶L¶›Ÿtþ­\¸páÈY´è!Ë4JJK°L+%4 ÓHÒ“\ÉV{ÔK'«ŸhRÒÞÖFSS–e=¿aÆ%………§äMçÝ“ùóçO.--íX²d‰÷·ÇÏ45²¯ëM'ôT ³IºtÂõ©fæ‚p‚äO°ù¥œ‘a¿¿Ž’Þ _}õÕ‚o|ãó[ZZf†B¡a÷ÃÔžÒ)Tž=—‰l’¤C¯ÂhkoçСCD"‘M&LøÅ믿¾zîé‰Â»/gÑ<ôvÝ*-ß›¥/ÒeS7’r°C$œ'á¼”‰\þ2ßçžþ9è•h™>WK–,±lÙ²©üŒmÛ%êáËûuA¢ghÑqœx Ø___¿áG?úÑúóÏ?ßûíÂ3u’ÿoA:á¼mÔËÞV—du¢äJÂyΓtÉ•M}Ìd³ù½’™H–-ÈžéÛ±lèÍHÿmˆhÿ{)<àm{#œ—O'WºÝ–SÂyq¸¾.‚´ã‰•æô­J¦çéGùÎ<ôóçûCºlªf¦ör?ἕÜÞ@=ˆ u¼ûk³ nýÁÉ ]ºÔÊ{Ëiü̓iWú<Òù ç»B´!r áD‘-DàÏŸñ2.§È´–Ò?tÕ1lô²MÏÓò! úç¹ì¯3[꫟AA& çå='ˆ_ÒõF¶L$êXC¤B6œH¨ SY ØŸþN*öÿ½´þÎÆÕ;«t.k~äí!;?ÀÛŸîZ¢Ôq‚–ÜYÁ:ßîw·óuËmMŽ|¬<äÈì¡ñ+3ëÝ9ðípâ-nù³V=F|˜IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/ie6.css0000664000175000017500000000136612203711415023766 0ustar takakitakaki* html img, * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none", this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')", this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''), this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')", this.runtimeStyle.backgroundImage = "none")),this.pngSet=true) );} #wrap{display:table;height:100%} pyramid-1.4.5/pyramid/scaffolds/starter/+package+/static/middlebg.png0000664000175000017500000000535512203711415025050 0ustar takakitakaki‰PNG  IHDR¬º—ƹ CiCCPICC Profilex–wTSYÀï{/½ÐB‘z MJ‘z‘^E%$B °WDWiŠ"‹".¸ºY+¢XX°/È" ¬‹«ˆŠe_ôeÿØý¾³óǜߛ;sïÜ™¹ç<(¾BQ&¬@†H"óñ`ÆÄÆ1ñÝD€ÖpyÙYAáÞ?/3u’±L Ïúuÿ¸Åò a2?›þ¥ÈËKÐBй|A6å<”Ós%Y2û$ÊôÄ4ËÑQV•qò6ÿìó…ÝdÌÏñQYÎYü ¾Œ;PÞ’# Œ¢œŸ#ä¢|eýti†å7(Ó3Ül0™]"ॠl…2EÆAyJò,NœÅÁ24O8™YËÅÂä Ó˜g´vtd3}¹é‰„Âå¥qÅ|&'3#‹+ZÀ—;Ë¢€’¬¶L´ÈöÖŽöö, ´ü_å_¿zý;ÈzûÅãeèçžAŒ®o¶o±ßl™Õ°§ÐÚìøfK, eª÷¾Ùô Ÿ@óY÷aÈæ%E"Ér²´ÌÍ͵ x²‚~•ÿéðÕóŸaÖy²ó¾ÖŽé)HâJÓ%LYQy™é™R13;‹Ë0Ybtëÿ8+­Yy˜‡ ’b=* 2¡(m·ˆ/”3EL¡èŸ:üÃfå Ã/s­æ# /± 7èù½ `hd€ÄïGW ¯} $FÙË‹Öý2÷(£ëŸõß\„~ÂÙÂd¦ÌÌ ‹`ò¤â£oB¦°€ä¨- Œ Øà Ü€ðÁ ĂŀR@ƒ\° ¬ù ì{@9¨5 4€ œÀepÜ}à>#à˜¯Á Axˆ Ñ 5H2€Ì ˆ ͇¼ @( Š… dHI¡UÐF¨*†Ê¡ƒPô#t º]…z »Ð4ý ½ƒ˜ÓaMض„Ù°;GÀ‹àdx)¼΃·Ã¥p5| n†/À×á>x~O!!# Da!l„ƒ#qH"FÖ H R4 mH'r D&·††abXgŒ/&ÃÃ,ŬÁlÔcŽ`š1˜[˜!Ì$æ#–ŠÕÀša°~Øl26›-ÁÖb›°—°}ØìkÇÀáp¾¸X\*n%nn®w׃ÆMáñx5¼ÞŒçâ%ø||þþ¾?‚C ´ 6oBADØ@(!%œ%ôF 3D¢щLä—‹ˆ5Ä6â âq†¤H2"¹"H©¤õ¤RRééé%™LÖ%;’CÉBò:r)ù8ù yˆü–¢D1¥p(ñ)e;å0å<å.å%•J5¤ºQã¨êvjõ"õõMÎBÎOŽ/·V®B®Y®W›¦°©iŠi…é 3ØÌÞLh¶Ï¬Çkîh.2¯6`QXî¬V=kÈ‚ah±Á¢Åâ¹¥¾eœåNËNËVvVéV5V÷­•¬ý­7X·Yÿicjó©°¹=—:×{îÚ¹­s_ØšÙ l÷ÛÞ±£ÙÙm¶k·û`ï`/¶o°wÐwHp¨t`ÓÙ!ìmì+ŽXGÇµŽ§ß:Ù;IœN8ýáÌrNs>ê<6Ïhž`^ͼa]®ËA—ÁùÌù óÌtÕqåºV»>vÓsã»Õºº›¸§ºsîaå!öhò˜æ8qVsÎ{"ž>žžÝ^J^‘^å^¼u½“½ë½'}ì|Vúœ÷ÅúøîôðÓôãùÕùMú;ø¯öï „”<4 ¶ÁAþA»‚,0X ZÐ ‚ý‚w? 1 Yòs(.4$´"ôI˜uت°ÎpZø’ð£á¯#<"Š"îGGJ#Û£ä£â£ê¢¦£=£‹£c,cVÇ\UƶÆáã¢âjã¦z-ܳp$Þ.>?¾‘Ñ¢e‹®.V_œ¾øÌù%Ü%'° Ñ GÞsƒ¹ÕÜ©D¿ÄÊÄI‡·—÷ŒïÆßÍ¸Š£I.IÅIcÉ.É»’ÇS\SJR&„a¹ðEªojUêtZpÚá´OéÑ鄌„ŒS"%Qš¨#S+sYfO–YV~ÖàR§¥{–NŠĵÙPö¢ìV ý™ê’K7I‡ræçTä¼ÉÊ=¹Lq™hY×rÓå[—®ð^ñýJÌJÞÊöU:«Ö¯Zí¾úàhMâšöµzkóÖŽ¬óYwd=i}Úú_6Xm(Þðjcôƶ<ͼuyÛ|6ÕçËå‹ó6;o®Ú‚Ù"ÜÒ½uîÖ²­ ø× ­ K ßoãm»öõw¥ß}Úž´½»È¾hÿÜÑŽþ®;+¯(Þ´«y7swÁîW{–ì¹Zb[Rµ—´Wºw°4°´µL¿lGÙûò”ò¾ ŠÆJÊ­•Óûøûz÷»ío¨Ò¬*¬zw@xàÎAŸƒÍÕ†Õ%‡p‡r=©‰ªéüžý}]­zmaí‡Ã¢ÃƒGÂŽtÔ9ÔÕÕ8ZT×KëÇÅ»ùƒç­ ¬†ƒŒÆÂãà¸ôøÓ~ì?p¢ý$ûdÃO?U6Ñš š¡æåÍ“-)-ƒ­±­=§üOµ·9·5ýlñóáÓ:§+Î(Ÿ):K:›wöÓ¹ç¦ÎgŸ¸|a¸}Iûý‹1ow„vt_ ¸tå²÷å‹î箸\9}Õéê©kìk-×í¯7wÙu5ýb÷KS·}wó ‡­7o¶õÌë9ÛëÚ{á–ç­Ë·ýn_ï[Ð×ÓÙg ~`ðÿÎØÝô»/îåÜ›¹¿îöAÁC…‡%4Uÿjòkã ýà™!Ï¡®Çáïó†Ÿý–ýÛû‘¼'Ô'%£Ú£uc6c§Ç½Ço>]øtäYÖ³™‰ü߯|nüü§?Üþ蚌™y!~ñéÏm/Õ^~eûª}*dêÑëŒ×3ÓoÔÞyË~Ûù.úÝèLî{üûÒ&Ú>||ð)ãÓ§¿›óüìÎçŠ pHYs  šœPIDAT(cøøñã& €ÿÿÿ‡²H#SØ4½ø¹8]E„¶A¢ÍTô¶àÄ&†ÍDˆaSB¬ë±9§%Hr3NÏ $,Â&…¨½´›IEND®B`‚pyramid-1.4.5/pyramid/scaffolds/starter/+package+/__init__.py0000664000175000017500000000052712203712502023407 0ustar takakitakakifrom pyramid.config import Configurator def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(settings=settings) config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') config.scan() return config.make_wsgi_app() pyramid-1.4.5/pyramid/scaffolds/starter/+package+/templates/0000775000175000017500000000000012210157153023273 5ustar takakitakakipyramid-1.4.5/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl0000664000175000017500000000670412210154276027067 0ustar takakitakaki The Pyramid Web Application Development Framework
pyramid

Welcome to ${project}, an application generated by
the Pyramid web application development framework.

pyramid-1.4.5/pyramid/scaffolds/starter/development.ini_tmpl0000664000175000017500000000213612203712502023632 0ustar takakitakaki### # app configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html ### [app:main] use = egg:{{project}} pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar # By default, the toolbar only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_{{package_logger}}] level = DEBUG handlers = qualname = {{package}} [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s pyramid-1.4.5/pyramid/wsgi.py0000664000175000017500000000617212210154276015460 0ustar takakitakakifrom functools import wraps from pyramid.request import call_app_with_subpath_as_path_info def wsgiapp(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` :term:`view callable`. This decorator differs from the :func:`pyramid.wsgi.wsgiapp2` decorator inasmuch as fixups of ``PATH_INFO`` and ``SCRIPT_NAME`` within the WSGI environment *are not* performed before the application is invoked. E.g., the following in a ``views.py`` module:: @wsgiapp def hello_world(environ, start_response): body = 'Hello world' start_response('200 OK', [ ('Content-Type', 'text/plain'), ('Content-Length', len(body)) ] ) return [body] Allows the following call to :meth:`pyramid.config.Configurator.add_view`:: from views import hello_world config.add_view(hello_world, name='hello_world.txt') The ``wsgiapp`` decorator will convert the result of the WSGI application to a :term:`Response` and return it to :app:`Pyramid` as if the WSGI app were a :mod:`pyramid` view. """ def decorator(context, request): return request.get_response(wrapped) # Support case where wrapped is a callable object instance if getattr(wrapped, '__name__', None): return wraps(wrapped)(decorator) return wraps(wrapped, ('__module__', '__doc__'))(decorator) def wsgiapp2(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` view callable. This decorator differs from the :func:`pyramid.wsgi.wsgiapp` decorator inasmuch as fixups of ``PATH_INFO`` and ``SCRIPT_NAME`` within the WSGI environment *are* performed before the application is invoked. E.g. the following in a ``views.py`` module:: @wsgiapp2 def hello_world(environ, start_response): body = 'Hello world' start_response('200 OK', [ ('Content-Type', 'text/plain'), ('Content-Length', len(body)) ] ) return [body] Allows the following call to :meth:`pyramid.config.Configurator.add_view`:: from views import hello_world config.add_view(hello_world, name='hello_world.txt') The ``wsgiapp2`` decorator will convert the result of the WSGI application to a Response and return it to :app:`Pyramid` as if the WSGI app were a :app:`Pyramid` view. The ``SCRIPT_NAME`` and ``PATH_INFO`` values present in the WSGI environment are fixed up before the application is invoked. In particular, a new WSGI environment is generated, and the :term:`subpath` of the request passed to ``wsgiapp2`` is used as the new request's ``PATH_INFO`` and everything preceding the subpath is used as the ``SCRIPT_NAME``. The new environment is passed to the downstream WSGI application.""" def decorator(context, request): return call_app_with_subpath_as_path_info(request, wrapped) # Support case where wrapped is a callable object instance if getattr(wrapped, '__name__', None): return wraps(wrapped)(decorator) return wraps(wrapped, ('__module__', '__doc__'))(decorator) pyramid-1.4.5/pyramid/urldispatch.py0000664000175000017500000002005012210154276017020 0ustar takakitakakiimport re from zope.interface import implementer from pyramid.interfaces import ( IRoutesMapper, IRoute, ) from pyramid.compat import ( PY3, native_, text_, text_type, string_types, binary_type, is_nonstr_iter, decode_path_info, ) from pyramid.exceptions import URLDecodeError from pyramid.traversal import ( quote_path_segment, split_path_info, ) _marker = object() @implementer(IRoute) class Route(object): def __init__(self, name, pattern, factory=None, predicates=(), pregenerator=None): self.pattern = pattern self.path = pattern # indefinite b/w compat, not in interface self.match, self.generate = _compile_route(pattern) self.name = name self.factory = factory self.predicates = predicates self.pregenerator = pregenerator @implementer(IRoutesMapper) class RoutesMapper(object): def __init__(self): self.routelist = [] self.routes = {} def has_routes(self): return bool(self.routelist) def get_routes(self): return self.routelist def get_route(self, name): return self.routes.get(name) def connect(self, name, pattern, factory=None, predicates=(), pregenerator=None, static=False): if name in self.routes: oldroute = self.routes[name] if oldroute in self.routelist: self.routelist.remove(oldroute) route = Route(name, pattern, factory, predicates, pregenerator) if not static: self.routelist.append(route) self.routes[name] = route return route def generate(self, name, kw): return self.routes[name].generate(kw) def __call__(self, request): environ = request.environ try: # empty if mounted under a path in mod_wsgi, for example path = decode_path_info(environ['PATH_INFO'] or '/') except KeyError: path = '/' except UnicodeDecodeError as e: raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason) for route in self.routelist: match = route.match(path) if match is not None: preds = route.predicates info = {'match':match, 'route':route} if preds and not all((p(info, request) for p in preds)): continue return info return {'route':None, 'match':None} # stolen from bobo and modified old_route_re = re.compile(r'(\:[_a-zA-Z]\w*)') star_at_end = re.compile(r'\*\w*$') # The tortuous nature of the regex named ``route_re`` below is due to the # fact that we need to support at least one level of "inner" squigglies # inside the expr of a {name:expr} pattern. This regex used to be just # (\{[a-zA-Z][^\}]*\}) but that choked when supplied with e.g. {foo:\d{4}}. route_re = re.compile(r'(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})') def update_pattern(matchobj): name = matchobj.group(0) return '{%s}' % name[1:] def _compile_route(route): # This function really wants to consume Unicode patterns natively, but if # someone passes us a bytestring, we allow it by converting it to Unicode # using the ASCII decoding. We decode it using ASCII because we don't # want to accept bytestrings with high-order characters in them here as # we have no idea what the encoding represents. if route.__class__ is not text_type: try: route = text_(route, 'ascii') except UnicodeDecodeError: raise ValueError( 'The pattern value passed to add_route must be ' 'either a Unicode string or a plain string without ' 'any non-ASCII characters (you provided %r).' % route) if old_route_re.search(route) and not route_re.search(route): route = old_route_re.sub(update_pattern, route) if not route.startswith('/'): route = '/' + route remainder = None if star_at_end.search(route): route, remainder = route.rsplit('*', 1) pat = route_re.split(route) # every element in "pat" will be Unicode (regardless of whether the # route_re regex pattern is itself Unicode or str) pat.reverse() rpat = [] gen = [] prefix = pat.pop() # invar: always at least one element (route='/'+route) # We want to generate URL-encoded URLs, so we url-quote the prefix, being # careful not to quote any embedded slashes. We have to replace '%' with # '%%' afterwards, as the strings that go into "gen" are used as string # replacement targets. gen.append(quote_path_segment(prefix, safe='/').replace('%', '%%')) # native rpat.append(re.escape(prefix)) # unicode while pat: name = pat.pop() # unicode name = name[1:-1] if ':' in name: # reg may contain colons as well, # so we must strictly split name into two parts name, reg = name.split(':', 1) else: reg = '[^/]+' gen.append('%%(%s)s' % native_(name)) # native name = '(?P<%s>%s)' % (name, reg) # unicode rpat.append(name) s = pat.pop() # unicode if s: rpat.append(re.escape(s)) # unicode # We want to generate URL-encoded URLs, so we url-quote this # literal in the pattern, being careful not to quote the embedded # slashes. We have to replace '%' with '%%' afterwards, as the # strings that go into "gen" are used as string replacement # targets. What is appended to gen is a native string. gen.append(quote_path_segment(s, safe='/').replace('%', '%%')) if remainder: rpat.append('(?P<%s>.*?)' % remainder) # unicode gen.append('%%(%s)s' % native_(remainder)) # native pattern = ''.join(rpat) + '$' # unicode match = re.compile(pattern).match def matcher(path): # This function really wants to consume Unicode patterns natively, # but if someone passes us a bytestring, we allow it by converting it # to Unicode using the ASCII decoding. We decode it using ASCII # because we don't want to accept bytestrings with high-order # characters in them here as we have no idea what the encoding # represents. if path.__class__ is not text_type: path = text_(path, 'ascii') m = match(path) if m is None: return None d = {} for k, v in m.groupdict().items(): # k and v will be Unicode 2.6.4 and lower doesnt accept unicode # kwargs as **kw, so we explicitly cast the keys to native # strings in case someone wants to pass the result as **kw nk = native_(k, 'ascii') if k == remainder: d[nk] = split_path_info(v) else: d[nk] = v return d gen = ''.join(gen) def generator(dict): newdict = {} for k, v in dict.items(): if PY3: # pragma: no cover if v.__class__ is binary_type: # url_quote below needs a native string, not bytes on Py3 v = v.decode('utf-8') else: if v.__class__ is text_type: # url_quote below needs bytes, not unicode on Py2 v = v.encode('utf-8') if k == remainder: # a stararg argument if is_nonstr_iter(v): v = '/'.join([quote_path_segment(x) for x in v]) # native else: if v.__class__ not in string_types: v = str(v) v = quote_path_segment(v, safe='/') else: if v.__class__ not in string_types: v = str(v) # v may be bytes (py2) or native string (py3) v = quote_path_segment(v) # at this point, the value will be a native string newdict[k] = v result = gen % newdict # native string result return result return matcher, generator pyramid-1.4.5/pyramid/request.py0000664000175000017500000004053012210154276016173 0ustar takakitakakiimport json from zope.deprecation import deprecate from zope.deprecation.deprecation import deprecated from zope.interface import implementer from zope.interface.interface import InterfaceClass from webob import BaseRequest from pyramid.interfaces import ( IRequest, IResponse, ISessionFactory, IResponseFactory, ) from pyramid.compat import ( iterkeys_, itervalues_, iteritems_, text_, bytes_, native_, ) from pyramid.decorator import reify from pyramid.response import Response from pyramid.url import URLMethodsMixin from pyramid.util import InstancePropertyMixin class TemplateContext(object): pass class DeprecatedRequestMethodsMixin(object): # b/c dict interface for "root factory" code that expects a bare # environ. Explicitly omitted dict methods: clear (unnecessary), # copy (implemented by WebOb), fromkeys (unnecessary); deprecated # as of Pyramid 1.1. dictlike = ('Use of the request as a dict-like object is deprecated as ' 'of Pyramid 1.1. Use dict-like methods of "request.environ" ' 'instead.') @deprecate(dictlike) def __contains__(self, k): return self.environ.__contains__(k) @deprecate(dictlike) def __delitem__(self, k): return self.environ.__delitem__(k) @deprecate(dictlike) def __getitem__(self, k): return self.environ.__getitem__(k) @deprecate(dictlike) def __iter__(self): return iter(self.environ) @deprecate(dictlike) def __setitem__(self, k, v): self.environ[k] = v @deprecate(dictlike) def get(self, k, default=None): return self.environ.get(k, default) @deprecate(dictlike) def has_key(self, k): return k in self.environ @deprecate(dictlike) def items(self): return self.environ.items() @deprecate(dictlike) def iteritems(self): return iteritems_(self.environ) @deprecate(dictlike) def iterkeys(self): return iterkeys_(self.environ) @deprecate(dictlike) def itervalues(self): return itervalues_(self.environ) @deprecate(dictlike) def keys(self): return self.environ.keys() @deprecate(dictlike) def pop(self, k): return self.environ.pop(k) @deprecate(dictlike) def popitem(self): return self.environ.popitem() @deprecate(dictlike) def setdefault(self, v, default): return self.environ.setdefault(v, default) @deprecate(dictlike) def update(self, v, **kw): return self.environ.update(v, **kw) @deprecate(dictlike) def values(self): return self.environ.values() # 1.0 deprecated bw compat code for using response_* values rr_dep = ('Accessing and setting "request.response_%s" is ' 'deprecated as of Pyramid 1.1; access or set ' '"request.response.%s" instead.') # response_content_type def _response_content_type_get(self): return self._response_content_type def _response_content_type_set(self, value): self._response_content_type = value def _response_content_type_del(self): del self._response_content_type response_content_type = property(_response_content_type_get, _response_content_type_set, _response_content_type_del) response_content_type = deprecated( response_content_type, rr_dep % ('content_type', 'content_type')) # response_headerlist def _response_headerlist_get(self): return self._response_headerlist def _response_headerlist_set(self, value): self._response_headerlist = value def _response_headerlist_del(self): del self._response_headerlist response_headerlist = property(_response_headerlist_get, _response_headerlist_set, _response_headerlist_del) hl_dep = ('Accessing and setting "request.response_headerlist" is ' 'deprecated as of Pyramid 1.1; access the headerlist via ' '"request.response.headerlist" and extend headers via ' '"request.response.headerlist.extend(alist)" instead of ' '"request.response_headerlist = alist"') response_headerlist = deprecated(response_headerlist, hl_dep) # response_status def _response_status_get(self): return self._response_status def _response_status_set(self, value): self._response_status = value def _response_status_del(self): del self._response_status response_status = property(_response_status_get, _response_status_set, _response_status_del) response_status = deprecated( response_status, rr_dep % ('status', 'status')) # response_charset def _response_charset_get(self): return self._response_charset def _response_charset_set(self, value): self._response_charset = value def _response_charset_del(self): del self._response_charset response_charset = property(_response_charset_get, _response_charset_set, _response_charset_del) response_charset = deprecated( response_charset, rr_dep % ('charset', 'charset')) # response_cache_for def _response_cache_for_get(self): return self._response_cache_for def _response_cache_for_set(self, value): self._response_cache_for = value def _response_cache_for_del(self): del self._response_cache_for response_cache_for = property(_response_cache_for_get, _response_cache_for_set, _response_cache_for_del) response_cache_for = deprecated( response_cache_for, rr_dep % ('cache_for', 'cache_expires')) class CallbackMethodsMixin(object): response_callbacks = () finished_callbacks = () def add_response_callback(self, callback): """ Add a callback to the set of callbacks to be called by the :term:`router` at a point after a :term:`response` object is successfully created. :app:`Pyramid` does not have a global response object: this functionality allows an application to register an action to be performed against the response once one is created. A 'callback' is a callable which accepts two positional parameters: ``request`` and ``response``. For example: .. code-block:: python :linenos: def cache_callback(request, response): 'Set the cache_control max_age for the response' response.cache_control.max_age = 360 request.add_response_callback(cache_callback) Response callbacks are called in the order they're added (first-to-most-recently-added). No response callback is called if an exception happens in application code, or if the response object returned by :term:`view` code is invalid. All response callbacks are called *after* the :class:`pyramid.events.NewResponse` event is sent. Errors raised by callbacks are not handled specially. They will be propagated to the caller of the :app:`Pyramid` router application. See also: :ref:`using_response_callbacks`. """ callbacks = self.response_callbacks if not callbacks: callbacks = [] callbacks.append(callback) self.response_callbacks = callbacks def _process_response_callbacks(self, response): callbacks = self.response_callbacks while callbacks: callback = callbacks.pop(0) callback(self, response) def add_finished_callback(self, callback): """ Add a callback to the set of callbacks to be called unconditionally by the :term:`router` at the very end of request processing. ``callback`` is a callable which accepts a single positional parameter: ``request``. For example: .. code-block:: python :linenos: import transaction def commit_callback(request): '''commit or abort the transaction associated with request''' if request.exception is not None: transaction.abort() else: transaction.commit() request.add_finished_callback(commit_callback) Finished callbacks are called in the order they're added ( first- to most-recently- added). Finished callbacks (unlike response callbacks) are *always* called, even if an exception happens in application code that prevents a response from being generated. The set of finished callbacks associated with a request are called *very late* in the processing of that request; they are essentially the last thing called by the :term:`router`. They are called after response processing has already occurred in a top-level ``finally:`` block within the router request processing code. As a result, mutations performed to the ``request`` provided to a finished callback will have no meaningful effect, because response processing will have already occurred, and the request's scope will expire almost immediately after all finished callbacks have been processed. Errors raised by finished callbacks are not handled specially. They will be propagated to the caller of the :app:`Pyramid` router application. See also: :ref:`using_finished_callbacks`. """ callbacks = self.finished_callbacks if not callbacks: callbacks = [] callbacks.append(callback) self.finished_callbacks = callbacks def _process_finished_callbacks(self): callbacks = self.finished_callbacks while callbacks: callback = callbacks.pop(0) callback(self) @implementer(IRequest) class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin, CallbackMethodsMixin, InstancePropertyMixin): """ A subclass of the :term:`WebOb` Request class. An instance of this class is created by the :term:`router` and is provided to a view callable (and to other subsystems) as the ``request`` argument. The documentation below (save for the ``add_response_callback`` and ``add_finished_callback`` methods, which are defined in this subclass itself, and the attributes ``context``, ``registry``, ``root``, ``subpath``, ``traversed``, ``view_name``, ``virtual_root`` , and ``virtual_root_path``, each of which is added to the request by the :term:`router` at request ingress time) are autogenerated from the WebOb source code used when this documentation was generated. Due to technical constraints, we can't yet display the WebOb version number from which this documentation is autogenerated, but it will be the 'prevailing WebOb version' at the time of the release of this :app:`Pyramid` version. See http://pythonpaste.org/webob/ for further information. """ exception = None exc_info = None matchdict = None matched_route = None ResponseClass = Response @reify def tmpl_context(self): # docs-deprecated template context for Pylons-like apps; do not # remove. return TemplateContext() @reify def session(self): """ Obtain the :term:`session` object associated with this request. If a :term:`session factory` has not been registered during application configuration, a :class:`pyramid.exceptions.ConfigurationError` will be raised""" factory = self.registry.queryUtility(ISessionFactory) if factory is None: raise AttributeError( 'No session factory registered ' '(see the Sessions chapter of the Pyramid documentation)') return factory(self) @reify def response(self): """This attribute is actually a "reified" property which returns an instance of the :class:`pyramid.response.Response`. class. The response object returned does not exist until this attribute is accessed. Once it is accessed, subsequent accesses will return the same Response object. The ``request.response`` API is used by renderers. A render obtains the response object it will return from a view that uses that renderer by accessing ``request.response``. Therefore, it's possible to use the ``request.response`` API to set up a response object with "the right" attributes (e.g. by calling ``request.response.set_cookie()``) within a view that uses a renderer. Mutations to this response object will be preserved in the response sent to the client.""" registry = self.registry response_factory = registry.queryUtility(IResponseFactory, default=Response) return response_factory() def is_response(self, ob): """ Return ``True`` if the object passed as ``ob`` is a valid response object, ``False`` otherwise.""" if ob.__class__ is Response: return True registry = self.registry adapted = registry.queryAdapterOrSelf(ob, IResponse) if adapted is None: return False return adapted is ob @property def json_body(self): return json.loads(text_(self.body, self.charset)) def route_request_iface(name, bases=()): # zope.interface treats the __name__ as the __doc__ and changes __name__ # to None for interfaces that contain spaces if you do not pass a # nonempty __doc__ (insane); see # zope.interface.interface.Element.__init__ and # https://github.com/Pylons/pyramid/issues/232; as a result, always pass # __doc__ to the InterfaceClass constructor. iface = InterfaceClass('%s_IRequest' % name, bases=bases, __doc__="route_request_iface-generated interface") # for exception view lookups iface.combined = InterfaceClass( '%s_combined_IRequest' % name, bases=(iface, IRequest), __doc__ = 'route_request_iface-generated combined interface') return iface def add_global_response_headers(request, headerlist): def add_headers(request, response): for k, v in headerlist: response.headerlist.append((k, v)) request.add_response_callback(add_headers) def call_app_with_subpath_as_path_info(request, app): # Copy the request. Use the source request's subpath (if it exists) as # the new request's PATH_INFO. Set the request copy's SCRIPT_NAME to the # prefix before the subpath. Call the application with the new request # and return a response. # # Postconditions: # - SCRIPT_NAME and PATH_INFO are empty or start with / # - At least one of SCRIPT_NAME or PATH_INFO are set. # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should # be '/'). environ = request.environ script_name = environ.get('SCRIPT_NAME', '') path_info = environ.get('PATH_INFO', '/') subpath = list(getattr(request, 'subpath', ())) new_script_name = '' # compute new_path_info new_path_info = '/' + '/'.join([native_(x.encode('utf-8'), 'latin-1') for x in subpath]) if new_path_info != '/': # don't want a sole double-slash if path_info != '/': # if orig path_info is '/', we're already done if path_info.endswith('/'): # readd trailing slash stripped by subpath (traversal) # conversion new_path_info += '/' # compute new_script_name workback = (script_name + path_info).split('/') tmp = [] while workback: if tmp == subpath: break el = workback.pop() if el: tmp.insert(0, text_(bytes_(el, 'latin-1'), 'utf-8')) # strip all trailing slashes from workback to avoid appending undue slashes # to end of script_name while workback and (workback[-1] == ''): workback = workback[:-1] new_script_name = '/'.join(workback) new_request = request.copy() new_request.environ['SCRIPT_NAME'] = new_script_name new_request.environ['PATH_INFO'] = new_path_info return new_request.get_response(app) pyramid-1.4.5/pyramid/asset.py0000664000175000017500000000253012203712502015612 0ustar takakitakakiimport os import pkg_resources from pyramid.compat import string_types from pyramid.path import ( package_path, package_name, ) def resolve_asset_spec(spec, pname='__main__'): if pname and not isinstance(pname, string_types): pname = pname.__name__ # as package if os.path.isabs(spec): return None, spec filename = spec if ':' in spec: pname, filename = spec.split(':', 1) elif pname is None: pname, filename = None, spec return pname, filename def asset_spec_from_abspath(abspath, package): """ Try to convert an absolute path to a resource in a package to a resource specification if possible; otherwise return the absolute path. """ if getattr(package, '__name__', None) == '__main__': return abspath pp = package_path(package) + os.path.sep if abspath.startswith(pp): relpath = abspath[len(pp):] return '%s:%s' % (package_name(package), relpath.replace(os.path.sep, '/')) return abspath # bw compat only; use pyramid.path.AssetDescriptor.abspath() instead def abspath_from_asset_spec(spec, pname='__main__'): if pname is None: return spec pname, filename = resolve_asset_spec(spec, pname) if pname is None: return filename return pkg_resources.resource_filename(pname, filename) pyramid-1.4.5/pyramid/fixers/0000775000175000017500000000000012210157153015424 5ustar takakitakakipyramid-1.4.5/pyramid/fixers/fix_bfg_imports.py0000664000175000017500000001551012203712502021156 0ustar takakitakakiimport os import re import sys from lib2to3.refactor import get_fixers_from_package from lib2to3.refactor import RefactoringTool from lib2to3.fixer_util import Name from lib2to3.fixer_util import attr_chain from lib2to3 import fixer_base MAPPING = {'repoze.bfg':'pyramid'} MODULE_NAMES = ( 'compat', 'configuration', 'authentication', 'authorization', 'chameleon_text', 'chameleon_zpt', 'decorator', 'encode', 'events', 'exceptions', 'i18n', 'includes', 'interfaces', 'location', 'log', 'paster', 'path', 'registry', 'renderers', 'request', 'resource', 'router', 'scripting', 'security', 'settings', 'static', 'testing', 'tests', 'tests.test_configuration', 'tests.ccbugapp', 'tests.exceptionviewapp', 'tests.exceptionviewapp.models', 'tests.fixtureapp', 'tests.fixtureapp.models', 'tests.grokkedapp', 'tests.hybridapp', 'tests.localeapp', 'tests.restbugapp', 'tests.routesapp', 'threadlocal', 'traversal', 'urldispatch', 'url', 'view', 'wsgi', 'zcml', ) for name in MODULE_NAMES: frm = 'repoze.bfg.' + name to = 'pyramid.' + name MAPPING[frm] = to def alternates(members): return "(" + "|".join(map(str, members)) + ")" def build_pattern(mapping=MAPPING): mod_list = [] for key in mapping: splitted = key.split('.') joined = " '.' ".join(["'%s'" %s for s in splitted]) mod_list.append(joined) mod_list = ' | '.join( ['module_name=dotted_name< %s >' %s for s in mod_list]) yield """name_import=import_name< 'import' ((%s) | multiple_imports=dotted_as_names< any* (%s) any* >) > """ % (mod_list, mod_list) yield """import_from< 'from' (%s) 'import' ['('] ( any | import_as_name< any 'as' any > | import_as_names< any* >) [')'] > """ % mod_list yield """import_name< 'import' (dotted_as_name< (%s) 'as' any > | multiple_imports=dotted_as_names< any* dotted_as_name< (%s) 'as' any > any* >) > """ % (mod_list, mod_list) # Find usages of module members in code e.g. ``repoze.bfg`` or # ``repoze.bfg.configuration`` # 'repoze' trailer< '.' 'bfg' > trailer< '.' 'configuration' > bare_names = [] for key in mapping: splitted = key.split('.') tmp = ["'%s'" % splitted[0]] for thing in splitted[1:]: tmp.append(" trailer< '.' '%s' > " % thing) bare_name = ''.join(tmp) bare_names.append(bare_name) names = alternates(bare_names) yield "power< bare_with_attr=%s >" % names class FixBfgImports(fixer_base.BaseFix): mapping = MAPPING run_order = 8 def build_pattern(self): pattern = "|".join(build_pattern(self.mapping)) return pattern def compile_pattern(self): # We override this, so MAPPING can be pragmatically altered and the # changes will be reflected in PATTERN. self.PATTERN = self.build_pattern() super(FixBfgImports, self).compile_pattern() # Don't match the node if it's within another match. def match(self, node): match = super(FixBfgImports, self).match results = match(node) if results: # Module usage could be in the trailer of an attribute lookup, so we # might have nested matches when "bare_with_attr" is present. if "bare_with_attr" not in results and \ any(match(obj) for obj in attr_chain(node, "parent")): return False return results return False def start_tree(self, tree, filename): super(FixBfgImports, self).start_tree(tree, filename) self.replace = {} def transform(self, node, results): # Mostly copied from fix_imports.py import_mod = results.get("module_name") if import_mod: try: mod_name = import_mod.value except AttributeError: # XXX: A hack to remove whitespace prefixes and suffixes mod_name = str(import_mod).strip() new_name = self.mapping[mod_name] import_mod.replace(Name(new_name, prefix=import_mod.prefix)) if "name_import" in results: # If it's not a "from x import x, y" or "import x as y" import, # marked its usage to be replaced. self.replace[mod_name] = new_name if "multiple_imports" in results: # This is a nasty hack to fix multiple imports on a line (e.g., # "import StringIO, urlparse"). The problem is that I can't # figure out an easy way to make a pattern recognize the keys of # MAPPING randomly sprinkled in an import statement. results = self.match(node) if results: self.transform(node, results) else: # Replace usage of the module. bare_name_text = ''.join(map(str,results['bare_with_attr'])).strip() new_name = self.replace.get(bare_name_text) bare_name = results["bare_with_attr"][0] if new_name: node.replace(Name(new_name, prefix=bare_name.prefix)) MODULE_ALTERNATIVES = [] for name in MODULE_NAMES: MODULE_ALTERNATIVES.append(r'\.' + re.escape(name)+r'[\w\.]*?') MODULE_ALTERNATIVES = '|'.join(MODULE_ALTERNATIVES) BFG_NS_RE = r'xmlns\s*?=\s*?[\'\"]http://namespaces\.repoze\.org/bfg[\'\"]' BFG_IN_ATTR = r'(repoze\.bfg)(%s)' % MODULE_ALTERNATIVES BFG_INCLUDE_IN_ATTR = r'repoze\.bfg\.includes' ATTR = re.compile(BFG_IN_ATTR, re.MULTILINE) INCLUDE_ATTR = re.compile(BFG_INCLUDE_IN_ATTR, re.MULTILINE) NS = re.compile(BFG_NS_RE, re.MULTILINE) def replace(match): return 'pyramid%s' % match.group(2) def fix_zcml(path): for root, dirs, files in os.walk(path): for file in files: if file.endswith('.zcml'): absfile = os.path.join(root, file) f = open(absfile, 'rb') text = f.read() f.close() newt = NS.sub('xmlns="http://pylonshq.com/pyramid"', text) newt = INCLUDE_ATTR.sub('pyramid_zcml', newt) newt = ATTR.sub(replace, newt) if text != newt: newf = open(absfile, 'wb') newf.write(newt) newf.flush() newf.close() for dir in dirs: if dir.startswith('.'): dirs.remove(dir) def main(argv=None): if argv is None: argv = sys.argv path = argv[1] fixer_names = get_fixers_from_package('pyramid.fixers') tool = RefactoringTool(fixer_names) tool.refactor([path], write=True) fix_zcml(path) if __name__ == '__main__': main() pyramid-1.4.5/pyramid/fixers/__init__.py0000664000175000017500000000001211752470445017542 0ustar takakitakaki# package pyramid-1.4.5/pyramid/i18n.py0000664000175000017500000003422412210154276015265 0ustar takakitakakiimport gettext import os from translationstring import ( Translator, Pluralizer, TranslationString, # API TranslationStringFactory, # API ) TranslationString = TranslationString # PyFlakes TranslationStringFactory = TranslationStringFactory # PyFlakes from pyramid.compat import PY3 from pyramid.interfaces import ( ILocalizer, ITranslationDirectories, ILocaleNegotiator, ) from pyramid.threadlocal import get_current_registry class Localizer(object): """ An object providing translation and pluralizations related to the current request's locale name. A :class:`pyramid.i18n.Localizer` object is created using the :func:`pyramid.i18n.get_localizer` function. """ def __init__(self, locale_name, translations): self.locale_name = locale_name self.translations = translations self.pluralizer = None self.translator = None def translate(self, tstring, domain=None, mapping=None): """ Translate a :term:`translation string` to the current language and interpolate any *replacement markers* in the result. The ``translate`` method accepts three arguments: ``tstring`` (required), ``domain`` (optional) and ``mapping`` (optional). When called, it will translate the ``tstring`` translation string to a ``unicode`` object using the current locale. If the current locale could not be determined, the result of interpolation of the default value is returned. The optional ``domain`` argument can be used to specify or override the domain of the ``tstring`` (useful when ``tstring`` is a normal string rather than a translation string). The optional ``mapping`` argument can specify or override the ``tstring`` interpolation mapping, useful when the ``tstring`` argument is a simple string instead of a translation string. Example:: from pyramid.18n import TranslationString ts = TranslationString('Add ${item}', domain='mypackage', mapping={'item':'Item'}) translated = localizer.translate(ts) Example:: translated = localizer.translate('Add ${item}', domain='mypackage', mapping={'item':'Item'}) """ if self.translator is None: self.translator = Translator(self.translations) return self.translator(tstring, domain=domain, mapping=mapping) def pluralize(self, singular, plural, n, domain=None, mapping=None): """ Return a Unicode string translation by using two :term:`message identifier` objects as a singular/plural pair and an ``n`` value representing the number that appears in the message using gettext plural forms support. The ``singular`` and ``plural`` objects passed may be translation strings or unicode strings. ``n`` represents the number of elements. ``domain`` is the translation domain to use to do the pluralization, and ``mapping`` is the interpolation mapping that should be used on the result. Note that if the objects passed are translation strings, their domains and mappings are ignored. The domain and mapping arguments must be used instead. If the ``domain`` is not supplied, a default domain is used (usually ``messages``). Example:: num = 1 translated = localizer.pluralize('Add ${num} item', 'Add ${num} items', num, mapping={'num':num}) """ if self.pluralizer is None: self.pluralizer = Pluralizer(self.translations) return self.pluralizer(singular, plural, n, domain=domain, mapping=mapping) def default_locale_negotiator(request): """ The default :term:`locale negotiator`. Returns a locale name or ``None``. - First, the negotiator looks for the ``_LOCALE_`` attribute of the request object (possibly set by a view or a listener for an :term:`event`). - Then it looks for the ``request.params['_LOCALE_']`` value. - Then it looks for the ``request.cookies['_LOCALE_']`` value. - Finally, the negotiator returns ``None`` if the locale could not be determined via any of the previous checks (when a locale negotiator returns ``None``, it signifies that the :term:`default locale name` should be used.) """ name = '_LOCALE_' locale_name = getattr(request, name, None) if locale_name is None: locale_name = request.params.get(name) if locale_name is None: locale_name = request.cookies.get(name) return locale_name def negotiate_locale_name(request): """ Negotiate and return the :term:`locale name` associated with the current request (never cached).""" try: registry = request.registry except AttributeError: registry = get_current_registry() negotiator = registry.queryUtility(ILocaleNegotiator, default=default_locale_negotiator) locale_name = negotiator(request) if locale_name is None: settings = registry.settings or {} locale_name = settings.get('default_locale_name', 'en') return locale_name def get_locale_name(request): """ Return the :term:`locale name` associated with the current request (possibly cached).""" locale_name = getattr(request, 'locale_name', None) if locale_name is None: locale_name = negotiate_locale_name(request) request.locale_name = locale_name return locale_name def make_localizer(current_locale_name, translation_directories): """ Create a :class:`pyramid.i18n.Localizer` object corresponding to the provided locale name from the translations found in the list of translation directories.""" translations = Translations() translations._catalog = {} locales_to_try = [] if '_' in current_locale_name: locales_to_try = [current_locale_name.split('_')[0]] locales_to_try.append(current_locale_name) # intent: order locales left to right in least specific to most specific, # e.g. ['de', 'de_DE']. This services the intent of creating a # translations object that returns a "more specific" translation for a # region, but will fall back to a "less specific" translation for the # locale if necessary. Ordering from least specific to most specific # allows us to call translations.add in the below loop to get this # behavior. for tdir in translation_directories: locale_dirs = [] for lname in locales_to_try: ldir = os.path.realpath(os.path.join(tdir, lname)) if os.path.isdir(ldir): locale_dirs.append(ldir) for locale_dir in locale_dirs: messages_dir = os.path.join(locale_dir, 'LC_MESSAGES') if not os.path.isdir(os.path.realpath(messages_dir)): continue for mofile in os.listdir(messages_dir): mopath = os.path.realpath(os.path.join(messages_dir, mofile)) if mofile.endswith('.mo') and os.path.isfile(mopath): with open(mopath, 'rb') as mofp: domain = mofile[:-3] dtrans = Translations(mofp, domain) translations.add(dtrans) return Localizer(locale_name=current_locale_name, translations=translations) def get_localizer(request): """ Retrieve a :class:`pyramid.i18n.Localizer` object corresponding to the current request's locale name. """ localizer = getattr(request, 'localizer', None) if localizer is None: # no locale object cached on request try: registry = request.registry except AttributeError: registry = get_current_registry() current_locale_name = get_locale_name(request) localizer = registry.queryUtility(ILocalizer, name=current_locale_name) if localizer is None: # no localizer utility registered yet tdirs = registry.queryUtility(ITranslationDirectories, default=[]) localizer = make_localizer(current_locale_name, tdirs) registry.registerUtility(localizer, ILocalizer, name=current_locale_name) request.localizer = localizer return localizer class Translations(gettext.GNUTranslations, object): """An extended translation catalog class (ripped off from Babel) """ DEFAULT_DOMAIN = 'messages' def __init__(self, fileobj=None, domain=DEFAULT_DOMAIN): """Initialize the translations catalog. :param fileobj: the file-like object the translation should be read from """ # germanic plural by default; self.plural will be overwritten by # GNUTranslations._parse (called as a side effect if fileobj is # passed to GNUTranslations.__init__) with a "real" self.plural for # this domain; see https://github.com/Pylons/pyramid/issues/235 self.plural = lambda n: int(n != 1) gettext.GNUTranslations.__init__(self, fp=fileobj) self.files = list(filter(None, [getattr(fileobj, 'name', None)])) self.domain = domain self._domains = {} @classmethod def load(cls, dirname=None, locales=None, domain=DEFAULT_DOMAIN): """Load translations from the given directory. :param dirname: the directory containing the ``MO`` files :param locales: the list of locales in order of preference (items in this list can be either `Locale` objects or locale strings) :param domain: the message domain :return: the loaded catalog, or a ``NullTranslations`` instance if no matching translations were found :rtype: `Translations` """ if locales is not None: if not isinstance(locales, (list, tuple)): locales = [locales] locales = [str(l) for l in locales] if not domain: domain = cls.DEFAULT_DOMAIN filename = gettext.find(domain, dirname, locales) if not filename: return gettext.NullTranslations() with open(filename, 'rb') as fp: return cls(fileobj=fp, domain=domain) def __repr__(self): return '<%s: "%s">' % (type(self).__name__, self._info.get('project-id-version')) def add(self, translations, merge=True): """Add the given translations to the catalog. If the domain of the translations is different than that of the current catalog, they are added as a catalog that is only accessible by the various ``d*gettext`` functions. :param translations: the `Translations` instance with the messages to add :param merge: whether translations for message domains that have already been added should be merged with the existing translations :return: the `Translations` instance (``self``) so that `merge` calls can be easily chained :rtype: `Translations` """ domain = getattr(translations, 'domain', self.DEFAULT_DOMAIN) if merge and domain == self.domain: return self.merge(translations) existing = self._domains.get(domain) if merge and existing is not None: existing.merge(translations) else: translations.add_fallback(self) self._domains[domain] = translations return self def merge(self, translations): """Merge the given translations into the catalog. Message translations in the specified catalog override any messages with the same identifier in the existing catalog. :param translations: the `Translations` instance with the messages to merge :return: the `Translations` instance (``self``) so that `merge` calls can be easily chained :rtype: `Translations` """ if isinstance(translations, gettext.GNUTranslations): self._catalog.update(translations._catalog) if isinstance(translations, Translations): self.files.extend(translations.files) return self def dgettext(self, domain, message): """Like ``gettext()``, but look the message up in the specified domain. """ return self._domains.get(domain, self).gettext(message) def ldgettext(self, domain, message): """Like ``lgettext()``, but look the message up in the specified domain. """ return self._domains.get(domain, self).lgettext(message) def dugettext(self, domain, message): """Like ``ugettext()``, but look the message up in the specified domain. """ if PY3: # pragma: no cover return self._domains.get(domain, self).gettext(message) else: # pragma: no cover return self._domains.get(domain, self).ugettext(message) def dngettext(self, domain, singular, plural, num): """Like ``ngettext()``, but look the message up in the specified domain. """ return self._domains.get(domain, self).ngettext(singular, plural, num) def ldngettext(self, domain, singular, plural, num): """Like ``lngettext()``, but look the message up in the specified domain. """ return self._domains.get(domain, self).lngettext(singular, plural, num) def dungettext(self, domain, singular, plural, num): """Like ``ungettext()`` but look the message up in the specified domain. """ if PY3: # pragma: no cover return self._domains.get(domain, self).ngettext( singular, plural, num) else: # pragma: no cover return self._domains.get(domain, self).ungettext( singular, plural, num) pyramid-1.4.5/pyramid/location.py0000664000175000017500000000516111752470445016325 0ustar takakitakaki############################################################################## # # Copyright (c) 2003 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## def inside(resource1, resource2): """Is ``resource1`` 'inside' ``resource2``? Return ``True`` if so, else ``False``. ``resource1`` is 'inside' ``resource2`` if ``resource2`` is a :term:`lineage` ancestor of ``resource1``. It is a lineage ancestor if its parent (or one of its parent's parents, etc.) is an ancestor. """ while resource1 is not None: if resource1 is resource2: return True resource1 = resource1.__parent__ return False def lineage(resource): """ Return a generator representing the :term:`lineage` of the :term:`resource` object implied by the ``resource`` argument. The generator first returns ``resource`` unconditionally. Then, if ``resource`` supplies a ``__parent__`` attribute, return the resource represented by ``resource.__parent__``. If *that* resource has a ``__parent__`` attribute, return that resource's parent, and so on, until the resource being inspected either has no ``__parent__`` attribute or which has a ``__parent__`` attribute of ``None``. For example, if the resource tree is:: thing1 = Thing() thing2 = Thing() thing2.__parent__ = thing1 Calling ``lineage(thing2)`` will return a generator. When we turn it into a list, we will get:: list(lineage(thing2)) [ , ] """ while resource is not None: yield resource # The common case is that the AttributeError exception below # is exceptional as long as the developer is a "good citizen" # who has a root object with a __parent__ of None. Using an # exception here instead of a getattr with a default is an # important micro-optimization, because this function is # called in any non-trivial application over and over again to # generate URLs and paths. try: resource = resource.__parent__ except AttributeError: resource = None pyramid-1.4.5/pyramid/interfaces.py0000664000175000017500000013472312210154276016636 0ustar takakitakakifrom zope.deprecation import deprecated from zope.interface import ( Attribute, Interface, ) from pyramid.compat import PY3 # public API interfaces class IContextFound(Interface): """ An event type that is emitted after :app:`Pyramid` finds a :term:`context` object but before it calls any view code. See the documentation attached to :class:`pyramid.events.ContextFound` for more information. .. note:: For backwards compatibility with versions of :app:`Pyramid` before 1.0, this event interface can also be imported as :class:`pyramid.interfaces.IAfterTraversal`. """ request = Attribute('The request object') IAfterTraversal = IContextFound class INewRequest(Interface): """ An event type that is emitted whenever :app:`Pyramid` begins to process a new request. See the documentation attached to :class:`pyramid.events.NewRequest` for more information.""" request = Attribute('The request object') class INewResponse(Interface): """ An event type that is emitted whenever any :app:`Pyramid` view returns a response. See the documentation attached to :class:`pyramid.events.NewResponse` for more information.""" request = Attribute('The request object') response = Attribute('The response object') class IApplicationCreated(Interface): """ Event issued when the :meth:`pyramid.config.Configurator.make_wsgi_app` method is called. See the documentation attached to :class:`pyramid.events.ApplicationCreated` for more information. .. note:: For backwards compatibility with :app:`Pyramid` versions before 1.0, this interface can also be imported as :class:`pyramid.interfaces.IWSGIApplicationCreatedEvent`. """ app = Attribute("Created application") IWSGIApplicationCreatedEvent = IApplicationCreated # b /c class IResponse(Interface): """ Represents a WSGI response using the WebOb response interface. Some attribute and method documentation of this interface references `RFC 2616 `_. This interface is most famously implemented by :class:`pyramid.response.Response` and the HTTP exception classes in :mod:`pyramid.httpexceptions`.""" RequestClass = Attribute( """ Alias for :class:`pyramid.request.Request` """) def __call__(environ, start_response): """ :term:`WSGI` call interface, should call the start_response callback and should return an iterable""" accept_ranges = Attribute( """Gets and sets and deletes the Accept-Ranges header. For more information on Accept-Ranges see RFC 2616, section 14.5""") age = Attribute( """Gets and sets and deletes the Age header. Converts using int. For more information on Age see RFC 2616, section 14.6.""") allow = Attribute( """Gets and sets and deletes the Allow header. Converts using list. For more information on Allow see RFC 2616, Section 14.7.""") app_iter = Attribute( """Returns the app_iter of the response. If body was set, this will create an app_iter from that body (a single-item list)""") def app_iter_range(start, stop): """ Return a new app_iter built from the response app_iter that serves up only the given start:stop range. """ body = Attribute( """The body of the response, as a str. This will read in the entire app_iter if necessary.""") body_file = Attribute( """A file-like object that can be used to write to the body. If you passed in a list app_iter, that app_iter will be modified by writes.""") cache_control = Attribute( """Get/set/modify the Cache-Control header (RFC 2616 section 14.9)""") cache_expires = Attribute( """ Get/set the Cache-Control and Expires headers. This sets the response to expire in the number of seconds passed when set. """) charset = Attribute( """Get/set the charset (in the Content-Type)""") def conditional_response_app(environ, start_response): """ Like the normal __call__ interface, but checks conditional headers: - If-Modified-Since (304 Not Modified; only on GET, HEAD) - If-None-Match (304 Not Modified; only on GET, HEAD) - Range (406 Partial Content; only on GET, HEAD)""" content_disposition = Attribute( """Gets and sets and deletes the Content-Disposition header. For more information on Content-Disposition see RFC 2616 section 19.5.1.""") content_encoding = Attribute( """Gets and sets and deletes the Content-Encoding header. For more information about Content-Encoding see RFC 2616 section 14.11.""") content_language = Attribute( """Gets and sets and deletes the Content-Language header. Converts using list. For more information about Content-Language see RFC 2616 section 14.12.""") content_length = Attribute( """Gets and sets and deletes the Content-Length header. For more information on Content-Length see RFC 2616 section 14.17. Converts using int. """) content_location = Attribute( """Gets and sets and deletes the Content-Location header. For more information on Content-Location see RFC 2616 section 14.14.""") content_md5 = Attribute( """Gets and sets and deletes the Content-MD5 header. For more information on Content-MD5 see RFC 2616 section 14.14.""") content_range = Attribute( """Gets and sets and deletes the Content-Range header. For more information on Content-Range see section 14.16. Converts using ContentRange object.""") content_type = Attribute( """Get/set the Content-Type header (or None), without the charset or any parameters. If you include parameters (or ; at all) when setting the content_type, any existing parameters will be deleted; otherwise they will be preserved.""") content_type_params = Attribute( """A dictionary of all the parameters in the content type. This is not a view, set to change, modifications of the dict would not be applied otherwise.""") def copy(): """ Makes a copy of the response and returns the copy. """ date = Attribute( """Gets and sets and deletes the Date header. For more information on Date see RFC 2616 section 14.18. Converts using HTTP date.""") def delete_cookie(key, path='/', domain=None): """ Delete a cookie from the client. Note that path and domain must match how the cookie was originally set. This sets the cookie to the empty string, and max_age=0 so that it should expire immediately. """ def encode_content(encoding='gzip', lazy=False): """ Encode the content with the given encoding (only gzip and identity are supported).""" environ = Attribute( """Get/set the request environ associated with this response, if any.""") etag = Attribute( """ Gets and sets and deletes the ETag header. For more information on ETag see RFC 2616 section 14.19. Converts using Entity tag.""") expires = Attribute( """ Gets and sets and deletes the Expires header. For more information on Expires see RFC 2616 section 14.21. Converts using HTTP date.""") headerlist = Attribute( """ The list of response headers. """) headers = Attribute( """ The headers in a dictionary-like object """) last_modified = Attribute( """ Gets and sets and deletes the Last-Modified header. For more information on Last-Modified see RFC 2616 section 14.29. Converts using HTTP date.""") location = Attribute( """ Gets and sets and deletes the Location header. For more information on Location see RFC 2616 section 14.30.""") def md5_etag(body=None, set_content_md5=False): """ Generate an etag for the response object using an MD5 hash of the body (the body parameter, or self.body if not given). Sets self.etag. If set_content_md5 is True sets self.content_md5 as well """ def merge_cookies(resp): """ Merge the cookies that were set on this response with the given resp object (which can be any WSGI application). If the resp is a webob.Response object, then the other object will be modified in-place. """ pragma = Attribute( """ Gets and sets and deletes the Pragma header. For more information on Pragma see RFC 2616 section 14.32. """) request = Attribute( """ Return the request associated with this response if any. """) retry_after = Attribute( """ Gets and sets and deletes the Retry-After header. For more information on Retry-After see RFC 2616 section 14.37. Converts using HTTP date or delta seconds.""") server = Attribute( """ Gets and sets and deletes the Server header. For more information on Server see RFC216 section 14.38. """) def set_cookie(key, value='', max_age=None, path='/', domain=None, secure=False, httponly=False, comment=None, expires=None, overwrite=False): """ Set (add) a cookie for the response """ status = Attribute( """ The status string. """) status_int = Attribute( """ The status as an integer """) unicode_body = Attribute( """ Get/set the unicode value of the body (using the charset of the Content-Type)""") def unset_cookie(key, strict=True): """ Unset a cookie with the given name (remove it from the response).""" vary = Attribute( """Gets and sets and deletes the Vary header. For more information on Vary see section 14.44. Converts using list.""") www_authenticate = Attribute( """ Gets and sets and deletes the WWW-Authenticate header. For more information on WWW-Authenticate see RFC 2616 section 14.47. Converts using 'parse_auth' and 'serialize_auth'. """) class IException(Interface): # not an API """ An interface representing a generic exception """ class IExceptionResponse(IException, IResponse): """ An interface representing a WSGI response which is also an exception object. Register an exception view using this interface as a ``context`` to apply the registered view for all exception types raised by :app:`Pyramid` internally (any exception that inherits from :class:`pyramid.response.Response`, including :class:`pyramid.httpexceptions.HTTPNotFound` and :class:`pyramid.httpexceptions.HTTPForbidden`).""" def prepare(environ): """ Prepares the response for being called as a WSGI application """ class IDict(Interface): # Documentation-only interface def __contains__(k): """ Return ``True`` if key ``k`` exists in the dictionary.""" def __setitem__(k, value): """ Set a key/value pair into the dictionary""" def __delitem__(k): """ Delete an item from the dictionary which is passed to the renderer as the renderer globals dictionary.""" def __getitem__(k): """ Return the value for key ``k`` from the dictionary or raise a KeyError if the key doesn't exist""" def __iter__(): """ Return an iterator over the keys of this dictionary """ def get(k, default=None): """ Return the value for key ``k`` from the renderer dictionary, or the default if no such value exists.""" def items(): """ Return a list of [(k,v)] pairs from the dictionary """ def keys(): """ Return a list of keys from the dictionary """ def values(): """ Return a list of values from the dictionary """ if not PY3: def iterkeys(): """ Return an iterator of keys from the dictionary """ def iteritems(): """ Return an iterator of (k,v) pairs from the dictionary """ def itervalues(): """ Return an iterator of values from the dictionary """ has_key = __contains__ def pop(k, default=None): """ Pop the key k from the dictionary and return its value. If k doesn't exist, and default is provided, return the default. If k doesn't exist and default is not provided, raise a KeyError.""" def popitem(): """ Pop the item with key k from the dictionary and return it as a two-tuple (k, v). If k doesn't exist, raise a KeyError.""" def setdefault(k, default=None): """ Return the existing value for key ``k`` in the dictionary. If no value with ``k`` exists in the dictionary, set the ``default`` value into the dictionary under the k name passed. If a value already existed in the dictionary, return it. If a value did not exist in the dictionary, return the default""" def update(d): """ Update the renderer dictionary with another dictionary ``d``.""" def clear(): """ Clear all values from the dictionary """ class IBeforeRender(IDict): """ Subscribers to this event may introspect and modify the set of :term:`renderer globals` before they are passed to a :term:`renderer`. The event object itself provides a dictionary-like interface for adding and removing :term:`renderer globals`. The keys and values of the dictionary are those globals. For example:: from repoze.events import subscriber from pyramid.interfaces import IBeforeRender @subscriber(IBeforeRender) def add_global(event): event['mykey'] = 'foo' See also :ref:`beforerender_event`. """ rendering_val = Attribute('The value returned by a view or passed to a ' '``render`` method for this rendering. ' 'This feature is new in Pyramid 1.2.') class IRenderer(Interface): def __call__(value, system): """ Call a the renderer implementation with the result of the view (``value``) passed in and return a result (a string or unicode object useful as a response body). Values computed by the system are passed by the system in the ``system`` parameter, which is a dictionary. Keys in the dictionary include: ``view`` (the view callable that returned the value), ``renderer_name`` (the template name or simple name of the renderer), ``context`` (the context object passed to the view), and ``request`` (the request object passed to the view).""" class ITemplateRenderer(IRenderer): def implementation(): """ Return the object that the underlying templating system uses to render the template; it is typically a callable that accepts arbitrary keyword arguments and returns a string or unicode object """ class IViewMapper(Interface): def __call__(self, object): """ Provided with an arbitrary object (a function, class, or instance), returns a callable with the call signature ``(context, request)``. The callable returned should itself return a Response object. An IViewMapper is returned by :class:`pyramid.interfaces.IViewMapperFactory`.""" class IViewMapperFactory(Interface): def __call__(self, **kw): """ Return an object which implements :class:`pyramid.interfaces.IViewMapper`. ``kw`` will be a dictionary containing view-specific arguments, such as ``permission``, ``predicates``, ``attr``, ``renderer``, and other items. An IViewMapperFactory is used by :meth:`pyramid.config.Configurator.add_view` to provide a plugpoint to extension developers who want to modify potential view callable invocation signatures and response values. """ class IAuthenticationPolicy(Interface): """ An object representing a Pyramid authentication policy. """ def authenticated_userid(request): """ Return the authenticated userid or ``None`` if no authenticated userid can be found. This method of the policy should ensure that a record exists in whatever persistent store is used related to the user (the user should not have been deleted); if a record associated with the current id does not exist in a persistent store, it should return ``None``.""" def unauthenticated_userid(request): """ Return the *unauthenticated* userid. This method performs the same duty as ``authenticated_userid`` but is permitted to return the userid based only on data present in the request; it needn't (and shouldn't) check any persistent store to ensure that the user record related to the request userid exists.""" def effective_principals(request): """ Return a sequence representing the effective principals including the userid and any groups belonged to by the current user, including 'system' groups such as Everyone and Authenticated. """ def remember(request, principal, **kw): """ Return a set of headers suitable for 'remembering' the principal named ``principal`` when set in a response. An individual authentication policy and its consumers can decide on the composition and meaning of ``**kw.`` """ def forget(request): """ Return a set of headers suitable for 'forgetting' the current user on subsequent requests. """ class IAuthorizationPolicy(Interface): """ An object representing a Pyramid authorization policy. """ def permits(context, principals, permission): """ Return ``True`` if any of the ``principals`` is allowed the ``permission`` in the current ``context``, else return ``False`` """ def principals_allowed_by_permission(context, permission): """ Return a set of principal identifiers allowed by the ``permission`` in ``context``. This behavior is optional; if you choose to not implement it you should define this method as something which raises a ``NotImplementedError``. This method will only be called when the ``pyramid.security.principals_allowed_by_permission`` API is used.""" class IMultiDict(IDict): # docs-only interface """ An ordered dictionary that can have multiple values for each key. A multidict adds the methods ``getall``, ``getone``, ``mixed``, ``extend`` ``add``, and ``dict_of_lists`` to the normal dictionary interface. A multidict data structure is used as ``request.POST``, ``request.GET``, and ``request.params`` within an :app:`Pyramid` application. """ def add(key, value): """ Add the key and value, not overwriting any previous value. """ def dict_of_lists(): """ Returns a dictionary where each key is associated with a list of values. """ def extend(other=None, **kwargs): """ Add a set of keys and values, not overwriting any previous values. The ``other`` structure may be a list of two-tuples or a dictionary. If ``**kwargs`` is passed, its value *will* overwrite existing values.""" def getall(key): """ Return a list of all values matching the key (may be an empty list) """ def getone(key): """ Get one value matching the key, raising a KeyError if multiple values were found. """ def mixed(): """ Returns a dictionary where the values are either single values, or a list of values when a key/value appears more than once in this dictionary. This is similar to the kind of dictionary often used to represent the variables in a web request. """ # internal interfaces class IRequest(Interface): """ Request type interface attached to all request objects """ class ITweens(Interface): """ Marker interface for utility registration representing the ordered set of a configuration's tween factories""" class IRequestHandler(Interface): """ """ def __call__(self, request): """ Must return a tuple of IReqest, IResponse or raise an exception. The ``request`` argument will be an instance of an object that provides IRequest.""" IRequest.combined = IRequest # for exception view lookups class IRequestExtensions(Interface): """ Marker interface for storing request extensions (properties and methods) which will be added to the request object.""" descriptors = Attribute( """A list of descriptors that will be added to each request.""") methods = Attribute( """A list of methods to be added to each request.""") class IRouteRequest(Interface): """ *internal only* interface used as in a utility lookup to find route-specific interfaces. Not an API.""" class IStaticURLInfo(Interface): """ A policy for generating URLs to static assets """ def add(config, name, spec, **extra): """ Add a new static info registration """ def generate(path, request, **kw): """ Generate a URL for the given path """ class IResponseFactory(Interface): """ A utility which generates a response factory """ def __call__(): """ Return a response factory (e.g. a callable that returns an object implementing IResponse, e.g. :class:`pyramid.response.Response`). It should accept all the arguments that the Pyramid Response class accepts.""" class IRequestFactory(Interface): """ A utility which generates a request """ def __call__(environ): """ Return an object implementing IRequest, e.g. an instance of ``pyramid.request.Request``""" def blank(path): """ Return an empty request object (see :meth:`pyramid.request.Request.blank`)""" class IViewClassifier(Interface): """ *Internal only* marker interface for views.""" class IExceptionViewClassifier(Interface): """ *Internal only* marker interface for exception views.""" class IView(Interface): def __call__(context, request): """ Must return an object that implements IResponse. """ class ISecuredView(IView): """ *Internal only* interface. Not an API. """ def __call_permissive__(context, request): """ Guaranteed-permissive version of __call__ """ def __permitted__(context, request): """ Return True if view execution will be permitted using the context and request, False otherwise""" class IMultiView(ISecuredView): """ *internal only*. A multiview is a secured view that is a collection of other views. Each of the views is associated with zero or more predicates. Not an API.""" def add(view, predicates, order, accept=None, phash=None): """ Add a view to the multiview. """ class IRootFactory(Interface): def __call__(request): """ Return a root object based on the request """ class IDefaultRootFactory(Interface): def __call__(request): """ Return the *default* root object for an application """ class ITraverser(Interface): def __call__(request): """ Return a dictionary with (at least) the keys ``root``, ``context``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. These values are typically the result of an object graph traversal. ``root`` is the physical root object, ``context`` will be a model object, ``view_name`` will be the view name used (a Unicode name), ``subpath`` will be a sequence of Unicode names that followed the view name but were not traversed, ``traversed`` will be a sequence of Unicode names that were traversed (including the virtual root path, if any) ``virtual_root`` will be a model object representing the virtual root (or the physical root if traversal was not performed), and ``virtual_root_path`` will be a sequence representing the virtual root path (a sequence of Unicode names) or ``None`` if traversal was not performed. Extra keys for special purpose functionality can be returned as necessary. All values returned in the dictionary will be made available as attributes of the ``request`` object by the :term:`router`. """ ITraverserFactory = ITraverser # b / c for 1.0 code class IRendererFactory(Interface): def __call__(info): """ Return an object that implements ``IRenderer``. ``info`` is an object that implement ``IRendererInfo``. """ class IRendererGlobalsFactory(Interface): def __call__(system_values): """ Return a dictionary of global renderer values (aka top-level template names). The ``system_values`` value passed in will be a dictionary that includes at least a ``request`` key, indicating the current request, and the value ``renderer_name``, which will be the name of the renderer in use.""" class IViewPermission(Interface): def __call__(context, request): """ Return True if the permission allows, return False if it denies. """ class IRouter(Interface): """WSGI application which routes requests to 'view' code based on a view registry.""" registry = Attribute( """Component architecture registry local to this application.""") class ISettings(Interface): """ Runtime settings utility for pyramid; represents the deployment settings for the application. Implements a mapping interface.""" # this interface, even if it becomes unused within Pyramid, is # imported by other packages (such as traversalwrapper) class ILocation(Interface): """Objects that have a structural location""" __parent__ = Attribute("The parent in the location hierarchy") __name__ = Attribute("The name within the parent") class IDebugLogger(Interface): """ Interface representing a PEP 282 logger """ ILogger = IDebugLogger # b/c class IRoutePregenerator(Interface): def __call__(request, elements, kw): """ A pregenerator is a function associated by a developer with a :term:`route`. The pregenerator for a route is called by :meth:`pyramid.request.Request.route_url` in order to adjust the set of arguments passed to it by the user for special purposes, such as Pylons 'subdomain' support. It will influence the URL returned by ``route_url``. A pregenerator should return a two-tuple of ``(elements, kw)`` after examining the originals passed to this function, which are the arguments ``(request, elements, kw)``. The simplest pregenerator is:: def pregenerator(request, elements, kw): return elements, kw You can employ a pregenerator by passing a ``pregenerator`` argument to the :meth:`pyramid.config.Configurator.add_route` function. """ class IRoute(Interface): """ Interface representing the type of object returned from ``IRoutesMapper.get_route``""" name = Attribute('The route name') pattern = Attribute('The route pattern') factory = Attribute( 'The :term:`root factory` used by the :app:`Pyramid` router ' 'when this route matches (or ``None``)') predicates = Attribute( 'A sequence of :term:`route predicate` objects used to ' 'determine if a request matches this route or not after ' 'basic pattern matching has been completed.') pregenerator = Attribute('This attribute should either be ``None`` or ' 'a callable object implementing the ' '``IRoutePregenerator`` interface') def match(path): """ If the ``path`` passed to this function can be matched by the ``pattern`` of this route, return a dictionary (the 'matchdict'), which will contain keys representing the dynamic segment markers in the pattern mapped to values extracted from the provided ``path``. If the ``path`` passed to this function cannot be matched by the ``pattern`` of this route, return ``None``. """ def generate(kw): """ Generate a URL based on filling in the dynamic segment markers in the pattern using the ``kw`` dictionary provided. """ class IRoutesMapper(Interface): """ Interface representing a Routes ``Mapper`` object """ def get_routes(): """ Return a sequence of Route objects registered in the mapper. Static routes will not be returned in this sequence.""" def has_routes(): """ Returns ``True`` if any route has been registered. """ def get_route(name): """ Returns an ``IRoute`` object if a route with the name ``name`` was registered, otherwise return ``None``.""" def connect(name, pattern, factory=None, predicates=(), pregenerator=None, static=True): """ Add a new route. """ def generate(name, kw): """ Generate a URL using the route named ``name`` with the keywords implied by kw""" def __call__(request): """ Return a dictionary containing matching information for the request; the ``route`` key of this dictionary will either be a Route object or ``None`` if no route matched; the ``match`` key will be the matchdict or ``None`` if no route matched. Static routes will not be considered for matching. """ class IResourceURL(Interface): virtual_path = Attribute('The virtual url path of the resource.') physical_path = Attribute('The physical url path of the resource.') class IContextURL(IResourceURL): """ An adapter which deals with URLs related to a context. ..warning:: This interface is deprecated as of Pyramid 1.3 with the introduction of IResourceURL. """ # this class subclasses IResourceURL because request.resource_url looks # for IResourceURL via queryAdapter. queryAdapter will find a deprecated # IContextURL registration if no registration for IResourceURL exists. # In reality, however, IContextURL objects were never required to have # the virtual_path or physical_path attributes spelled in IResourceURL. # The inheritance relationship is purely to benefit adapter lookup, # not to imply an inheritance relationship of interface attributes # and methods. # # Mechanics: # # class Fudge(object): # def __init__(self, one, two): # print one, two # class Another(object): # def __init__(self, one, two): # print one, two # ob = object() # r.registerAdapter(Fudge, (Interface, Interface), IContextURL) # print r.queryMultiAdapter((ob, ob), IResourceURL) # r.registerAdapter(Another, (Interface, Interface), IResourceURL) # print r.queryMultiAdapter((ob, ob), IResourceURL) # # prints # # # <__main__.Fudge object at 0x1cda890> # # <__main__.Another object at 0x1cda850> def virtual_root(): """ Return the virtual root related to a request and the current context""" def __call__(): """ Return a URL that points to the context. """ deprecated( 'IContextURL', 'As of Pyramid 1.3 the, "pyramid.interfaces.IContextURL" interface 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.' ) class IPEP302Loader(Interface): """ See http://www.python.org/dev/peps/pep-0302/#id30. """ def get_data(path): """ Retrieve data for and arbitrary "files" from storage backend. Raise IOError for not found. Data is returned as bytes. """ def is_package(fullname): """ Return True if the module specified by 'fullname' is a package. """ def get_code(fullname): """ Return the code object for the module identified by 'fullname'. Return 'None' if it's a built-in or extension module. If the loader doesn't have the code object but it does have the source code, return the compiled source code. Raise ImportError if the module can't be found by the importer at all. """ def get_source(fullname): """ Return the source code for the module identified by 'fullname'. Return a string, using newline characters for line endings, or None if the source is not available. Raise ImportError if the module can't be found by the importer at all. """ def get_filename(fullname): """ Return the value of '__file__' if the named module was loaded. If the module is not found, raise ImportError. """ class IPackageOverrides(IPEP302Loader): """ Utility for pkg_resources overrides """ # VH_ROOT_KEY is an interface; its imported from other packages (e.g. # traversalwrapper) VH_ROOT_KEY = 'HTTP_X_VHM_ROOT' class IChameleonLookup(Interface): translate = Attribute('IChameleonTranslate object') debug = Attribute('The ``debug_templates`` setting for this application') auto_reload = Attribute('The ``reload_templates`` setting for this app') def __call__(self, info): """ Return an ITemplateRenderer based on IRendererInfo ``info`` """ class IChameleonTranslate(Interface): """ Internal interface representing a chameleon translate function """ def __call__(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): """ Translate a mess of arguments to a Unicode object """ class ILocalizer(Interface): """ Localizer for a specific language """ class ILocaleNegotiator(Interface): def __call__(request): """ Return a locale name """ class ITranslationDirectories(Interface): """ A list object representing all known translation directories for an application""" class IDefaultPermission(Interface): """ A string object representing the default permission to be used for all view configurations which do not explicitly declare their own.""" class ISessionFactory(Interface): """ An interface representing a factory which accepts a request object and returns an ISession object """ def __call__(request): """ Return an ISession object """ class ISession(IDict): """ An interface representing a session (a web session object, usually accessed via ``request.session``. Keys and values of a session must be pickleable. """ # attributes created = Attribute('Integer representing Epoch time when created.') new = Attribute('Boolean attribute. If ``True``, the session is new.') # special methods def invalidate(): """ Invalidate the session. The action caused by ``invalidate`` is implementation-dependent, but it should have the effect of completely dissociating any data stored in the session with the current request. It might set response values (such as one which clears a cookie), or it might not.""" def changed(): """ Mark the session as changed. A user of a session should call this method after he or she mutates a mutable object that is *a value of the session* (it should not be required after mutating the session itself). For example, if the user has stored a dictionary in the session under the key ``foo``, and he or she does ``session['foo'] = {}``, ``changed()`` needn't be called. However, if subsequently he or she does ``session['foo']['a'] = 1``, ``changed()`` must be called for the sessioning machinery to notice the mutation of the internal dictionary.""" def flash(msg, queue='', allow_duplicate=True): """ Push a flash message onto the end of the flash queue represented by ``queue``. An alternate flash message queue can used by passing an optional ``queue``, which must be a string. If ``allow_duplicate`` is false, if the ``msg`` already exists in the queue, it will not be readded.""" def pop_flash(queue=''): """ Pop a queue from the flash storage. The queue is removed from flash storage after this message is called. The queue is returned; it is a list of flash messages added by :meth:`pyramid.interfaces.ISession.flash`""" def peek_flash(queue=''): """ Peek at a queue in the flash storage. The queue remains in flash storage after this message is called. The queue is returned; it is a list of flash messages added by :meth:`pyramid.interfaces.ISession.flash` """ def new_csrf_token(): """ Create and set into the session a new, random cross-site request forgery protection token. Return the token. It will be a string.""" def get_csrf_token(): """ Return a random cross-site request forgery protection token. It will be a string. If a token was previously added to the session via ``new_csrf_token``, that token will be returned. If no CSRF token was previously set into the session, ``new_csrf_token`` will be called, which will create and set a token, and this token will be returned. """ class IRendererInfo(Interface): """ An object implementing this interface is passed to every :term:`renderer factory` constructor as its only argument (conventionally named ``info``)""" name = Attribute('The value passed by the user as the renderer name') package = Attribute('The "current package" when the renderer ' 'configuration statement was found') type = Attribute('The renderer type name') registry = Attribute('The "current" application registry when the ' 'renderer was created') settings = Attribute('The deployment settings dictionary related ' 'to the current application') class IIntrospector(Interface): def get(category_name, discriminator, default=None): """ Get the IIntrospectable related to the category_name and the discriminator (or discriminator hash) ``discriminator``. If it does not exist in the introspector, return the value of ``default`` """ def get_category(category_name, default=None, sort_key=None): """ Get a sequence of dictionaries in the form ``[{'introspectable':IIntrospectable, 'related':[sequence of related IIntrospectables]}, ...]`` where each introspectable is part of the category associated with ``category_name`` . If the category named ``category_name`` does not exist in the introspector the value passed as ``default`` will be returned. If ``sort_key`` is ``None``, the sequence will be returned in the order the introspectables were added to the introspector. Otherwise, sort_key should be a function that accepts an IIntrospectable and returns a value from it (ala the ``key`` function of Python's ``sorted`` callable).""" def categories(): """ Return a sorted sequence of category names known by this introspector """ def categorized(sort_key=None): """ Get a sequence of tuples in the form ``[(category_name, [{'introspectable':IIntrospectable, 'related':[sequence of related IIntrospectables]}, ...])]`` representing all known introspectables. If ``sort_key`` is ``None``, each introspectables sequence will be returned in the order the introspectables were added to the introspector. Otherwise, sort_key should be a function that accepts an IIntrospectable and returns a value from it (ala the ``key`` function of Python's ``sorted`` callable).""" def remove(category_name, discriminator): """ Remove the IIntrospectable related to ``category_name`` and ``discriminator`` from the introspector, and fix up any relations that the introspectable participates in. This method will not raise an error if an introspectable related to the category name and discriminator does not exist.""" def related(intr): """ Return a sequence of IIntrospectables related to the IIntrospectable ``intr``. Return the empty sequence if no relations for exist.""" def add(intr): """ Add the IIntrospectable ``intr`` (use instead of :meth:`pyramid.interfaces.IIntrospector.add` when you have a custom IIntrospectable). Replaces any existing introspectable registered using the same category/discriminator. This method is not typically called directly, instead it's called indirectly by :meth:`pyramid.interfaces.IIntrospector.register`""" def relate(*pairs): """ Given any number of ``(category_name, discriminator)`` pairs passed as positional arguments, relate the associated introspectables to each other. The introspectable related to each pair must have already been added via ``.add`` or ``.add_intr``; a :exc:`KeyError` will result if this is not true. An error will not be raised if any pair has already been associated with another. This method is not typically called directly, instead it's called indirectly by :meth:`pyramid.interfaces.IIntrospector.register` """ def unrelate(*pairs): """ Given any number of ``(category_name, discriminator)`` pairs passed as positional arguments, unrelate the associated introspectables from each other. The introspectable related to each pair must have already been added via ``.add`` or ``.add_intr``; a :exc:`KeyError` will result if this is not true. An error will not be raised if any pair is not already related to another. This method is not typically called directly, instead it's called indirectly by :meth:`pyramid.interfaces.IIntrospector.register` """ class IIntrospectable(Interface): """ An introspectable object used for configuration introspection. In addition to the methods below, objects which implement this interface must also implement all the methods of Python's ``collections.MutableMapping`` (the "dictionary interface"), and must be hashable.""" title = Attribute('Text title describing this introspectable') type_name = Attribute('Text type name describing this introspectable') order = Attribute('integer order in which registered with introspector ' '(managed by introspector, usually)') category_name = Attribute('introspection category name') discriminator = Attribute('introspectable discriminator (within category) ' '(must be hashable)') discriminator_hash = Attribute('an integer hash of the discriminator') action_info = Attribute('An IActionInfo object representing the caller ' 'that invoked the creation of this introspectable ' '(usually a sentinel until updated during ' 'self.register)') def relate(category_name, discriminator): """ Indicate an intent to relate this IIntrospectable with another IIntrospectable (the one associated with the ``category_name`` and ``discriminator``) during action execution. """ def unrelate(category_name, discriminator): """ Indicate an intent to break the relationship between this IIntrospectable with another IIntrospectable (the one associated with the ``category_name`` and ``discriminator``) during action execution. """ def register(introspector, action_info): """ Register this IIntrospectable with an introspector. This method is invoked during action execution. Adds the introspectable and its relations to the introspector. ``introspector`` should be an object implementing IIntrospector. ``action_info`` should be a object implementing the interface :class:`pyramid.interfaces.IActionInfo` representing the call that registered this introspectable. Pseudocode for an implementation of this method: .. code-block:: python def register(self, introspector, action_info): self.action_info = action_info introspector.add(self) for methodname, category_name, discriminator in self._relations: method = getattr(introspector, methodname) method((i.category_name, i.discriminator), (category_name, discriminator)) """ def __hash__(): """ Introspectables must be hashable. The typical implementation of an introsepectable's __hash__ is:: return hash((self.category_name,) + (self.discriminator,)) """ class IActionInfo(Interface): """ Class which provides code introspection capability associated with an action. The ParserInfo class used by ZCML implements the same interface.""" file = Attribute( 'Filename of action-invoking code as a string') line = Attribute( 'Starting line number in file (as an integer) of action-invoking code.' 'This will be ``None`` if the value could not be determined.') def __str__(): """ Return a representation of the action information (including source code from file, if possible) """ class IAssetDescriptor(Interface): """ Describes an :term:`asset`. """ def absspec(): """ Returns the absolute asset specification for this asset (e.g. ``mypackage:templates/foo.pt``). """ def abspath(): """ Returns an absolute path in the filesystem to the asset. """ def stream(): """ Returns an input stream for reading asset contents. Raises an exception if the asset is a directory or does not exist. """ def isdir(): """ Returns True if the asset is a directory, otherwise returns False. """ def listdir(): """ Returns iterable of filenames of directory contents. Raises an exception if asset is not a directory. """ def exists(): """ Returns True if asset exists, otherwise returns False. """ class IJSONAdapter(Interface): """ Marker interface for objects that can convert an arbitrary object into a JSON-serializable primitive. """ class IPredicateList(Interface): """ Interface representing a predicate list """ # configuration phases: a lower phase number means the actions associated # with this phase will be executed earlier than those with later phase # numbers. The default phase number is 0, FTR. PHASE1_CONFIG = -20 PHASE2_CONFIG = -10 pyramid-1.4.5/pyramid/util.py0000664000175000017500000004147412210154276015470 0ustar takakitakakiimport functools import inspect import traceback import weakref from zope.interface import implementer from pyramid.exceptions import ( ConfigurationError, CyclicDependencyError, ) from pyramid.compat import ( iteritems_, is_nonstr_iter, integer_types, string_types, text_, PY3, ) from pyramid.interfaces import IActionInfo from pyramid.path import DottedNameResolver as _DottedNameResolver class DottedNameResolver(_DottedNameResolver): def __init__(self, package=None): # default to package = None for bw compat return _DottedNameResolver.__init__(self, package) class InstancePropertyMixin(object): """ Mixin that will allow an instance to add properties at run-time as if they had been defined via @property or @reify on the class itself. """ @classmethod def _make_property(cls, callable, name=None, reify=False): """ Convert a callable into one suitable for adding to the instance. This will return a 2-tuple containing the computed (name, property) pair. """ is_property = isinstance(callable, property) if is_property: fn = callable if name is None: raise ValueError('must specify "name" for a property') if reify: raise ValueError('cannot reify a property') elif name is not None: fn = lambda this: callable(this) fn.__name__ = name fn.__doc__ = callable.__doc__ else: name = callable.__name__ fn = callable if reify: import pyramid.decorator # avoid circular import fn = pyramid.decorator.reify(fn) elif not is_property: fn = property(fn) return name, fn def _set_properties(self, properties): """ Create several properties on the instance at once. This is a more efficient version of :meth:`pyramid.util.InstancePropertyMixin.set_property` which can accept multiple ``(name, property)`` pairs generated via :meth:`pyramid.util.InstancePropertyMixin._make_property`. ``attrs`` is a sequence of 2-tuples *or* a data structure with an ``.items()`` method which returns a sequence of 2-tuples (presumably a dictionary). It will be used to add several properties to the instance in a manner that is more efficient than simply calling ``set_property`` repeatedly. """ if hasattr(properties, 'items'): attrs = properties.items() else: attrs = properties attrs = dict(properties) parent = self.__class__ cls = type(parent.__name__, (parent, object), attrs) self.__class__ = cls def _set_extensions(self, extensions): for name, fn in iteritems_(extensions.methods): method = fn.__get__(self, self.__class__) setattr(self, name, method) self._set_properties(extensions.descriptors) def set_property(self, callable, name=None, reify=False): """ Add a callable or a property descriptor to the instance. Properties, unlike attributes, are lazily evaluated by executing an underlying callable when accessed. They can be useful for adding features to an object without any cost if those features go unused. A property may also be reified via the :class:`pyramid.decorator.reify` decorator by setting ``reify=True``, allowing the result of the evaluation to be cached. Using this method, the value of the property is only computed once for the lifetime of the object. ``callable`` can either be a callable that accepts the instance as its single positional parameter, or it can be a property descriptor. If the ``callable`` is a property descriptor, the ``name`` parameter must be supplied or a ``ValueError`` will be raised. Also note that a property descriptor cannot be reified, so ``reify`` must be ``False``. If ``name`` is None, the name of the property will be computed from the name of the ``callable``. .. code-block:: python :linenos: class Foo(InstancePropertyMixin): _x = 1 def _get_x(self): return _x def _set_x(self, value): self._x = value foo = Foo() foo.set_property(property(_get_x, _set_x), name='x') foo.set_property(_get_x, name='y', reify=True) >>> foo.x 1 >>> foo.y 1 >>> foo.x = 5 >>> foo.x 5 >>> foo.y # notice y keeps the original value 1 """ prop = self._make_property(callable, name=name, reify=reify) self._set_properties([prop]) class WeakOrderedSet(object): """ Maintain a set of items. Each item is stored as a weakref to avoid extending their lifetime. The values may be iterated over or the last item added may be accessed via the ``last`` property. If items are added more than once, the most recent addition will be remembered in the order: order = WeakOrderedSet() order.add('1') order.add('2') order.add('1') list(order) == ['2', '1'] order.last == '1' """ def __init__(self): self._items = {} self._order = [] def add(self, item): """ Add an item to the set.""" oid = id(item) if oid in self._items: self._order.remove(oid) self._order.append(oid) return ref = weakref.ref(item, lambda x: self.remove(item)) self._items[oid] = ref self._order.append(oid) def remove(self, item): """ Remove an item from the set.""" oid = id(item) if oid in self._items: del self._items[oid] self._order.remove(oid) def empty(self): """ Clear all objects from the set.""" self._items = {} self._order = [] def __len__(self): return len(self._order) def __contains__(self, item): oid = id(item) return oid in self._items def __iter__(self): return (self._items[oid]() for oid in self._order) @property def last(self): if self._order: oid = self._order[-1] return self._items[oid]() def strings_differ(string1, string2): """Check whether two strings differ while avoiding timing attacks. This function returns True if the given strings differ and False if they are equal. It's careful not to leak information about *where* they differ as a result of its running time, which can be very important to avoid certain timing-related crypto attacks: http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf """ if len(string1) != len(string2): return True invalid_bits = 0 for a, b in zip(string1, string2): invalid_bits += a != b return invalid_bits != 0 def object_description(object): """ Produce a human-consumable text description of ``object``, usually involving a Python dotted name. For example: .. code-block:: python >>> object_description(None) u'None' >>> from xml.dom import minidom >>> object_description(minidom) u'module xml.dom.minidom' >>> object_description(minidom.Attr) u'class xml.dom.minidom.Attr' >>> object_description(minidom.Attr.appendChild) u'method appendChild of class xml.dom.minidom.Attr' >>> If this method cannot identify the type of the object, a generic description ala ``object `` will be returned. If the object passed is already a string, it is simply returned. If it is a boolean, an integer, a list, a tuple, a set, or ``None``, a (possibly shortened) string representation is returned. """ if isinstance(object, string_types): return text_(object) if isinstance(object, integer_types): return text_(str(object)) if isinstance(object, (bool, float, type(None))): return text_(str(object)) if isinstance(object, set): if PY3: # pragma: no cover return shortrepr(object, '}') else: return shortrepr(object, ')') if isinstance(object, tuple): return shortrepr(object, ')') if isinstance(object, list): return shortrepr(object, ']') if isinstance(object, dict): return shortrepr(object, '}') module = inspect.getmodule(object) if module is None: return text_('object %s' % str(object)) modulename = module.__name__ if inspect.ismodule(object): return text_('module %s' % modulename) if inspect.ismethod(object): oself = getattr(object, '__self__', None) if oself is None: # pragma: no cover oself = getattr(object, 'im_self', None) return text_('method %s of class %s.%s' % (object.__name__, modulename, oself.__class__.__name__)) if inspect.isclass(object): dottedname = '%s.%s' % (modulename, object.__name__) return text_('class %s' % dottedname) if inspect.isfunction(object): dottedname = '%s.%s' % (modulename, object.__name__) return text_('function %s' % dottedname) return text_('object %s' % str(object)) def shortrepr(object, closer): r = str(object) if len(r) > 100: r = r[:100] + ' ... %s' % closer return r class Sentinel(object): def __init__(self, repr): self.repr = repr def __repr__(self): return self.repr FIRST = Sentinel('FIRST') LAST = Sentinel('LAST') class TopologicalSorter(object): """ A utility class which can be used to perform topological sorts against tuple-like data.""" def __init__( self, default_before=LAST, default_after=None, first=FIRST, last=LAST, ): self.names = [] self.req_before = set() self.req_after = set() self.name2before = {} self.name2after = {} self.name2val = {} self.order = [] self.default_before = default_before self.default_after = default_after self.first = first self.last = last def remove(self, name): """ Remove a node from the sort input """ self.names.remove(name) del self.name2val[name] after = self.name2after.pop(name, []) if after: self.req_after.remove(name) for u in after: self.order.remove((u, name)) before = self.name2before.pop(name, []) if before: self.req_before.remove(name) for u in before: self.order.remove((name, u)) def add(self, name, val, after=None, before=None): """ Add a node to the sort input. The ``name`` should be a string or any other hashable object, the ``val`` should be the sortable (doesn't need to be hashable). ``after`` and ``before`` represents the name of one of the other sortables (or a sequence of such named) or one of the special sentinel values :attr:`pyramid.util.FIRST`` or :attr:`pyramid.util.LAST` representing the first or last positions respectively. ``FIRST`` and ``LAST`` can also be part of a sequence passed as ``before`` or ``after``. A sortable should not be added after LAST or before FIRST. An example:: sorter = TopologicalSorter() sorter.add('a', {'a':1}, before=LAST, after='b') sorter.add('b', {'b':2}, before=LAST, after='c') sorter.add('c', {'c':3}) sorter.sorted() # will be {'c':3}, {'b':2}, {'a':1} """ if name in self.names: self.remove(name) self.names.append(name) self.name2val[name] = val if after is None and before is None: before = self.default_before after = self.default_after if after is not None: if not is_nonstr_iter(after): after = (after,) self.name2after[name] = after self.order += [(u, name) for u in after] self.req_after.add(name) if before is not None: if not is_nonstr_iter(before): before = (before,) self.name2before[name] = before self.order += [(name, o) for o in before] self.req_before.add(name) def sorted(self): """ Returns the sort input values in topologically sorted order""" order = [(self.first, self.last)] roots = [] graph = {} names = [self.first, self.last] names.extend(self.names) for a, b in self.order: order.append((a, b)) def add_node(node): if not node in graph: roots.append(node) graph[node] = [0] # 0 = number of arcs coming into this node def add_arc(fromnode, tonode): graph[fromnode].append(tonode) graph[tonode][0] += 1 if tonode in roots: roots.remove(tonode) for name in names: add_node(name) has_before, has_after = set(), set() for a, b in order: if a in names and b in names: # deal with missing dependencies add_arc(a, b) has_before.add(a) has_after.add(b) if not self.req_before.issubset(has_before): raise ConfigurationError( 'Unsatisfied before dependencies: %s' % (', '.join(sorted(self.req_before - has_before))) ) if not self.req_after.issubset(has_after): raise ConfigurationError( 'Unsatisfied after dependencies: %s' % (', '.join(sorted(self.req_after - has_after))) ) sorted_names = [] while roots: root = roots.pop(0) sorted_names.append(root) children = graph[root][1:] for child in children: arcs = graph[child][0] arcs -= 1 graph[child][0] = arcs if arcs == 0: roots.insert(0, child) del graph[root] if graph: # loop in input cycledeps = {} for k, v in graph.items(): cycledeps[k] = v[1:] raise CyclicDependencyError(cycledeps) result = [] for name in sorted_names: if name in self.names: result.append((name, self.name2val[name])) return result def viewdefaults(wrapped): """ Decorator for add_view-like methods which takes into account __view_defaults__ attached to view it is passed. Not a documented API but used by some external systems.""" def wrapper(self, *arg, **kw): defaults = {} if arg: view = arg[0] else: view = kw.get('view') view = self.maybe_dotted(view) if inspect.isclass(view): defaults = getattr(view, '__view_defaults__', {}).copy() if not '_backframes' in kw: kw['_backframes'] = 1 # for action_method defaults.update(kw) return wrapped(self, *arg, **defaults) return functools.wraps(wrapped)(wrapper) @implementer(IActionInfo) class ActionInfo(object): def __init__(self, file, line, function, src): self.file = file self.line = line self.function = function self.src = src def __str__(self): srclines = self.src.split('\n') src = '\n'.join(' %s' % x for x in srclines) return 'Line %s of file %s:\n%s' % (self.line, self.file, src) def action_method(wrapped): """ Wrapper to provide the right conflict info report data when a method that calls Configurator.action calls another that does the same. Not a documented API but used by some external systems.""" def wrapper(self, *arg, **kw): if self._ainfo is None: self._ainfo = [] info = kw.pop('_info', None) # backframes for outer decorators to actionmethods backframes = kw.pop('_backframes', 0) + 2 if is_nonstr_iter(info) and len(info) == 4: # _info permitted as extract_stack tuple info = ActionInfo(*info) if info is None: try: f = traceback.extract_stack(limit=3) info = ActionInfo(*f[-backframes]) except: # pragma: no cover info = ActionInfo(None, 0, '', '') self._ainfo.append(info) try: result = wrapped(self, *arg, **kw) finally: self._ainfo.pop() return result if hasattr(wrapped, '__name__'): functools.update_wrapper(wrapper, wrapped) wrapper.__docobj__ = wrapped return wrapper pyramid-1.4.5/pyramid/tweens.py0000664000175000017500000000357212203712502016007 0ustar takakitakakiimport sys from pyramid.interfaces import ( IExceptionViewClassifier, IRequest, IView, ) from zope.interface import providedBy def excview_tween_factory(handler, registry): """ A :term:`tween` factory which produces a tween that catches an exception raised by downstream tweens (or the main Pyramid request handler) and, if possible, converts it into a Response using an :term:`exception view`.""" adapters = registry.adapters def excview_tween(request): attrs = request.__dict__ try: response = handler(request) except Exception as exc: # WARNING: do not assign the result of sys.exc_info() to a # local var here, doing so will cause a leak attrs['exc_info'] = sys.exc_info() attrs['exception'] = exc # clear old generated request.response, if any; it may # have been mutated by the view, and its state is not # sane (e.g. caching headers) if 'response' in attrs: del attrs['response'] # we use .get instead of .__getitem__ below due to # https://github.com/Pylons/pyramid/issues/700 request_iface = attrs.get('request_iface', IRequest) provides = providedBy(exc) for_ = (IExceptionViewClassifier, request_iface.combined, provides) view_callable = adapters.lookup(for_, IView, default=None) if view_callable is None: raise response = view_callable(exc, request) finally: # prevent leakage (wrt exc_info) if 'exc_info' in attrs: del attrs['exc_info'] if 'exception' in attrs: del attrs['exception'] return response return excview_tween MAIN = 'MAIN' INGRESS = 'INGRESS' EXCVIEW = 'pyramid.tweens.excview_tween_factory' pyramid-1.4.5/pyramid/exceptions.py0000664000175000017500000000476512210154276016676 0ustar takakitakakifrom pyramid.httpexceptions import ( HTTPNotFound, HTTPForbidden, ) NotFound = HTTPNotFound # bw compat Forbidden = HTTPForbidden # bw compat CR = '\n' class PredicateMismatch(HTTPNotFound): """ Internal exception (not an API) raised by multiviews when no view matches. This exception subclasses the ``NotFound`` exception only one reason: if it reaches the main exception handler, it should be treated like a ``NotFound`` by any exception view registrations. """ class URLDecodeError(UnicodeDecodeError): """ This exception is raised when :app:`Pyramid` cannot successfully decode a URL or a URL path segment. This exception it behaves just like the Python builtin :exc:`UnicodeDecodeError`. It is a subclass of the builtin :exc:`UnicodeDecodeError` exception only for identity purposes, mostly so an exception view can be registered when a URL cannot be decoded. """ class ConfigurationError(Exception): """ Raised when inappropriate input values are supplied to an API method of a :term:`Configurator`""" class ConfigurationConflictError(ConfigurationError): """ Raised when a configuration conflict is detected during action processing""" def __init__(self, conflicts): self._conflicts = conflicts def __str__(self): r = ["Conflicting configuration actions"] items = sorted(self._conflicts.items()) for discriminator, infos in items: r.append(" For: %s" % (discriminator, )) for info in infos: for line in str(info).rstrip().split(CR): r.append(" "+line) return CR.join(r) class ConfigurationExecutionError(ConfigurationError): """An error occurred during execution of a configuration action """ def __init__(self, etype, evalue, info): self.etype, self.evalue, self.info = etype, evalue, info def __str__(self): return "%s: %s\n in:\n %s" % (self.etype, self.evalue, self.info) class CyclicDependencyError(Exception): """ The exception raised when the Pyramid topological sorter detects a cyclic dependency.""" def __init__(self, cycles): self.cycles = cycles def __str__(self): L = [] cycles = self.cycles for cycle in cycles: dependent = cycle dependees = cycles[cycle] L.append('%r sorts before %r' % (dependent, dependees)) msg = 'Implicit ordering cycle:' + '; '.join(L) return msg pyramid-1.4.5/pyramid/mako_templating.py0000664000175000017500000002234112210154301017643 0ustar takakitakakiimport os import posixpath import sys import threading import warnings from zope.interface import ( implementer, Interface, ) from pyramid.asset import ( resolve_asset_spec, abspath_from_asset_spec, ) from pyramid.compat import ( is_nonstr_iter, reraise, ) from pyramid.interfaces import ITemplateRenderer from pyramid.settings import asbool from pyramid.util import DottedNameResolver def _no_mako(*arg, **kw): # pragma: no cover raise NotImplementedError( "'mako' package is not importable (maybe downgrade MarkupSafe to " "0.16 or below if you're using Python 3.2)" ) try: from mako.lookup import TemplateLookup except (ImportError, SyntaxError, AttributeError): #pragma NO COVER class TemplateLookup(object): def __init__(self, **kw): for name in ('adjust_uri', 'get_template', 'filename_to_uri', 'put_string', 'put_template'): setattr(self, name, _no_mako) self.filesystem_checks = False try: from mako.exceptions import TopLevelLookupException except (ImportError, SyntaxError, AttributeError): #pragma NO COVER class TopLevelLookupException(Exception): pass try: from mako.exceptions import text_error_template except (ImportError, SyntaxError, AttributeError): #pragma NO COVER def text_error_template(lookup=None): _no_mako() class IMakoLookup(Interface): pass class PkgResourceTemplateLookup(TemplateLookup): """TemplateLookup subclass that handles asset specification URIs""" def adjust_uri(self, uri, relativeto): """Called from within a Mako template, avoids adjusting the uri if it looks like an asset specification""" # Don't adjust asset spec names isabs = os.path.isabs(uri) if (not isabs) and (':' in uri): return uri if not(isabs) and ('$' in uri): return uri.replace('$', ':') if relativeto is not None: relativeto = relativeto.replace('$', ':') if not(':' in uri) and (':' in relativeto): if uri.startswith('/'): return uri pkg, relto = relativeto.split(':') _uri = posixpath.join(posixpath.dirname(relto), uri) return '{0}:{1}'.format(pkg, _uri) if not(':' in uri) and not(':' in relativeto): return posixpath.join(posixpath.dirname(relativeto), uri) return TemplateLookup.adjust_uri(self, uri, relativeto) def get_template(self, uri): """Fetch a template from the cache, or check the filesystem for it In addition to the basic filesystem lookup, this subclass will use pkg_resource to load a file using the asset specification syntax. """ isabs = os.path.isabs(uri) if (not isabs) and (':' in uri): # Windows can't cope with colons in filenames, so we replace the # colon with a dollar sign in the filename mako uses to actually # store the generated python code in the mako module_directory or # in the temporary location of mako's modules adjusted = uri.replace(':', '$') try: if self.filesystem_checks: return self._check(adjusted, self._collection[adjusted]) else: return self._collection[adjusted] except KeyError: pname, path = resolve_asset_spec(uri) srcfile = abspath_from_asset_spec(path, pname) if os.path.isfile(srcfile): return self._load(srcfile, adjusted) raise TopLevelLookupException( "Can not locate template for uri %r" % uri) return TemplateLookup.get_template(self, uri) registry_lock = threading.Lock() class MakoRendererFactoryHelper(object): def __init__(self, settings_prefix=None): self.settings_prefix = settings_prefix def __call__(self, info): defname = None asset, ext = info.name.rsplit('.', 1) if '#' in asset: asset, defname = asset.rsplit('#', 1) path = '%s.%s' % (asset, ext) registry = info.registry settings = info.settings settings_prefix = self.settings_prefix if settings_prefix is None: settings_prefix = info.type +'.' lookup = registry.queryUtility(IMakoLookup, name=settings_prefix) def sget(name, default=None): return settings.get(settings_prefix + name, default) if lookup is None: reload_templates = settings.get('pyramid.reload_templates', None) if reload_templates is None: reload_templates = settings.get('reload_templates', False) reload_templates = asbool(reload_templates) directories = sget('directories', []) module_directory = sget('module_directory', None) input_encoding = sget('input_encoding', 'utf-8') error_handler = sget('error_handler', None) default_filters = sget('default_filters', 'h') imports = sget('imports', None) strict_undefined = asbool(sget('strict_undefined', False)) preprocessor = sget('preprocessor', None) if not is_nonstr_iter(directories): directories = list(filter(None, directories.splitlines())) directories = [ abspath_from_asset_spec(d) for d in directories ] if module_directory is not None: module_directory = abspath_from_asset_spec(module_directory) if error_handler is not None: dotted = DottedNameResolver(info.package) error_handler = dotted.maybe_resolve(error_handler) if default_filters is not None: if not is_nonstr_iter(default_filters): default_filters = list(filter( None, default_filters.splitlines())) if imports is not None: if not is_nonstr_iter(imports): imports = list(filter(None, imports.splitlines())) if preprocessor is not None: dotted = DottedNameResolver(info.package) preprocessor = dotted.maybe_resolve(preprocessor) lookup = PkgResourceTemplateLookup( directories=directories, module_directory=module_directory, input_encoding=input_encoding, error_handler=error_handler, default_filters=default_filters, imports=imports, filesystem_checks=reload_templates, strict_undefined=strict_undefined, preprocessor=preprocessor ) with registry_lock: registry.registerUtility(lookup, IMakoLookup, name=settings_prefix) return MakoLookupTemplateRenderer(path, defname, lookup) renderer_factory = MakoRendererFactoryHelper('mako.') class MakoRenderingException(Exception): def __init__(self, text): self.text = text def __repr__(self): return self.text __str__ = __repr__ @implementer(ITemplateRenderer) class MakoLookupTemplateRenderer(object): """ Render a :term:`Mako` template using the template implied by the ``path`` argument.The ``path`` argument may be a package-relative path, an absolute path, or a :term:`asset specification`. If a defname is defined, in the form of package:path/to/template#defname.mako, a function named ``defname`` inside the template will then be rendered. """ def __init__(self, path, defname, lookup): self.path = path self.defname = defname self.lookup = lookup def implementation(self): return self.lookup.get_template(self.path) def __call__(self, value, system): context = system.pop('context', None) if context is not None: system['_context'] = context # tuple returned to be deprecated if isinstance(value, tuple): warnings.warn( 'Using a tuple in the form (\'defname\', {}) to render a ' 'Mako partial will be deprecated in the future. Use a ' 'Mako template renderer as documented in the "Using A ' 'Mako def name Within a Renderer Name" chapter of the ' 'Pyramid narrative documentation instead', DeprecationWarning, 3) self.defname, value = value try: system.update(value) except (TypeError, ValueError): raise ValueError('renderer was passed non-dictionary as value') template = self.implementation() if self.defname is not None: template = template.get_def(self.defname) try: result = template.render_unicode(**system) except: try: exc_info = sys.exc_info() errtext = text_error_template().render( error=exc_info[1], traceback=exc_info[2] ) reraise(MakoRenderingException(errtext), None, exc_info[2]) finally: del exc_info return result pyramid-1.4.5/pyramid/__init__.py0000664000175000017500000000001212203712502016223 0ustar takakitakaki# package pyramid-1.4.5/pyramid/chameleon_zpt.py0000664000175000017500000000302512210154301017316 0ustar takakitakakifrom zope.interface import implementer from pyramid.interfaces import ITemplateRenderer from pyramid.decorator import reify from pyramid import renderers from chameleon.zpt.template import PageTemplateFile def renderer_factory(info): return renderers.template_renderer_factory(info, ZPTTemplateRenderer) class PyramidPageTemplateFile(PageTemplateFile): def cook(self, body): PageTemplateFile.cook(self, body) if self.macro: # render only the portion of the template included in a # define-macro named the value of self.macro macro_renderer = self.macros[self.macro].include self._render = macro_renderer @implementer(ITemplateRenderer) class ZPTTemplateRenderer(object): def __init__(self, path, lookup, macro=None): self.path = path self.lookup = lookup self.macro = macro @reify # avoid looking up reload_templates before manager pushed def template(self): tf = PyramidPageTemplateFile( self.path, auto_reload=self.lookup.auto_reload, debug=self.lookup.debug, translate=self.lookup.translate, macro=self.macro, ) return tf 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/threadlocal.py0000664000175000017500000000375212210154276016772 0ustar takakitakakiimport threading from pyramid.registry import global_registry class ThreadLocalManager(threading.local): def __init__(self, default=None): # http://code.google.com/p/google-app-engine-django/issues/detail?id=119 # we *must* use a keyword argument for ``default`` here instead # of a positional argument to work around a bug in the # implementation of _threading_local.local in Python, which is # used by GAE instead of _thread.local self.stack = [] self.default = default def push(self, info): self.stack.append(info) set = push # b/c def pop(self): if self.stack: return self.stack.pop() def get(self): try: return self.stack[-1] except IndexError: return self.default() def clear(self): self.stack[:] = [] def defaults(): return {'request':None, 'registry':global_registry} manager = ThreadLocalManager(default=defaults) def get_current_request(): """Return the currently active request or ``None`` if no request is currently active. This function should be used *extremely sparingly*, usually only in unit testing code. it's almost always usually a mistake to use ``get_current_request`` outside a testing context because its usage makes it possible to write code that can be neither easily tested nor scripted. """ return manager.get()['request'] def get_current_registry(context=None): # context required by getSiteManager API """Return the currently active :term:`application registry` or the global application registry if no request is currently active. This function should be used *extremely sparingly*, usually only in unit testing code. it's almost always usually a mistake to use ``get_current_registry`` outside a testing context because its usage makes it possible to write code that can be neither easily tested nor scripted. """ return manager.get()['registry'] pyramid-1.4.5/pyramid/router.py0000664000175000017500000002125512210154276016026 0ustar takakitakakifrom zope.interface import ( implementer, providedBy, ) from pyramid.interfaces import ( IDebugLogger, IRequest, IRequestExtensions, IRootFactory, IRouteRequest, IRouter, IRequestFactory, IRoutesMapper, ITraverser, IView, IViewClassifier, ITweens, ) from pyramid.events import ( ContextFound, NewRequest, NewResponse, ) from pyramid.httpexceptions import HTTPNotFound from pyramid.request import Request from pyramid.threadlocal import manager from pyramid.traversal import ( DefaultRootFactory, ResourceTreeTraverser, ) from pyramid.tweens import excview_tween_factory @implementer(IRouter) class Router(object): debug_notfound = False debug_routematch = False threadlocal_manager = manager def __init__(self, registry): q = registry.queryUtility self.logger = q(IDebugLogger) self.root_factory = q(IRootFactory, default=DefaultRootFactory) self.routes_mapper = q(IRoutesMapper) self.request_factory = q(IRequestFactory, default=Request) self.request_extensions = q(IRequestExtensions) tweens = q(ITweens) if tweens is None: tweens = excview_tween_factory self.orig_handle_request = self.handle_request self.handle_request = tweens(self.handle_request, registry) self.root_policy = self.root_factory # b/w compat self.registry = registry settings = registry.settings if settings is not None: self.debug_notfound = settings['debug_notfound'] self.debug_routematch = settings['debug_routematch'] def handle_request(self, request): attrs = request.__dict__ registry = attrs['registry'] request.request_iface = IRequest context = None routes_mapper = self.routes_mapper debug_routematch = self.debug_routematch adapters = registry.adapters has_listeners = registry.has_listeners notify = registry.notify logger = self.logger has_listeners and notify(NewRequest(request)) # find the root object root_factory = self.root_factory if routes_mapper is not None: info = routes_mapper(request) match, route = info['match'], info['route'] if route is None: if debug_routematch: msg = ('no route matched for url %s' % request.url) logger and logger.debug(msg) else: attrs['matchdict'] = match attrs['matched_route'] = route if debug_routematch: msg = ( 'route matched for url %s; ' 'route_name: %r, ' 'path_info: %r, ' 'pattern: %r, ' 'matchdict: %r, ' 'predicates: %r' % ( request.url, route.name, request.path_info, route.pattern, match, ', '.join([p.text() for p in route.predicates])) ) logger and logger.debug(msg) request.request_iface = registry.queryUtility( IRouteRequest, name=route.name, default=IRequest) root_factory = route.factory or self.root_factory root = root_factory(request) attrs['root'] = root # find a context traverser = adapters.queryAdapter(root, ITraverser) if traverser is None: traverser = ResourceTreeTraverser(root) tdict = traverser(request) context, view_name, subpath, traversed, vroot, vroot_path = ( tdict['context'], tdict['view_name'], tdict['subpath'], tdict['traversed'], tdict['virtual_root'], tdict['virtual_root_path'] ) attrs.update(tdict) has_listeners and notify(ContextFound(request)) # find a view callable context_iface = providedBy(context) view_callable = adapters.lookup( (IViewClassifier, request.request_iface, context_iface), IView, name=view_name, default=None) # invoke the view callable if view_callable is None: if self.debug_notfound: msg = ( 'debug_notfound of url %s; path_info: %r, ' 'context: %r, view_name: %r, subpath: %r, ' 'traversed: %r, root: %r, vroot: %r, ' 'vroot_path: %r' % ( request.url, request.path_info, context, view_name, subpath, traversed, root, vroot, vroot_path) ) logger and logger.debug(msg) else: msg = request.path_info raise HTTPNotFound(msg) else: response = view_callable(context, request) return response def invoke_subrequest(self, request, use_tweens=False): """ Obtain a response object from the Pyramid application based on information in the ``request`` object provided. The ``request`` object must be an object that implements the Pyramid request interface (such as a :class:`pyramid.request.Request` instance). If ``use_tweens`` is ``True``, the request will be sent to the :term:`tween` in the tween stack closest to the request ingress. If ``use_tweens`` is ``False``, the request will be sent to the main router handler, and no tweens will be invoked. This function also: - manages the threadlocal stack (so that :func:`~pyramid.threadlocal.get_current_request` and :func:`~pyramid.threadlocal.get_current_registry` work during a request) - Adds a ``registry`` attribute and a ``invoke_subrequest`` attribute (a callable) to the request object it's handed. - sets request extensions (such as those added via :meth:`~pyramid.config.Configurator.add_request_method` or :meth:`~pyramid.config.Configurator.set_request_property`) on the request it's passed. - causes a :class:`~pyramid.event.NewRequest` event to be sent at the beginning of request processing. - causes a :class:`~pyramid.event.ContextFound` event to be sent when a context resource is found. - causes a :class:`~pyramid.event.NewResponse` event to be sent when the Pyramid application returns a response. - Calls any :term:`response callback` functions defined within the request's lifetime if a response is obtained from the Pyramid application. - Calls any :term:`finished callback` functions defined within the request's lifetime. See also :ref:`subrequest_chapter`. """ registry = self.registry has_listeners = self.registry.has_listeners notify = self.registry.notify threadlocals = {'registry':registry, 'request':request} manager = self.threadlocal_manager manager.push(threadlocals) request.registry = registry request.invoke_subrequest = self.invoke_subrequest if use_tweens: handle_request = self.handle_request else: handle_request = self.orig_handle_request try: try: extensions = self.request_extensions if extensions is not None: request._set_extensions(extensions) response = handle_request(request) has_listeners and notify(NewResponse(request, response)) if request.response_callbacks: request._process_response_callbacks(response) return response finally: if request.finished_callbacks: request._process_finished_callbacks() finally: manager.pop() def __call__(self, environ, start_response): """ Accept ``environ`` and ``start_response``; create a :term:`request` and route the request to a :app:`Pyramid` view based on introspection of :term:`view configuration` within the application registry; call ``start_response`` and return an iterable. """ request = self.request_factory(environ) response = self.invoke_subrequest(request, use_tweens=True) return response(request.environ, start_response) pyramid-1.4.5/pyramid/resource.py0000664000175000017500000000036512203712502016326 0ustar takakitakaki""" Backwards compatibility shim module (forever). """ from pyramid.asset import * # b/w compat resolve_resource_spec = resolve_asset_spec resource_spec_from_abspath = asset_spec_from_abspath abspath_from_resource_spec = abspath_from_asset_spec pyramid-1.4.5/pyramid/scripts/0000775000175000017500000000000012210157153015613 5ustar takakitakakipyramid-1.4.5/pyramid/scripts/prequest.py0000664000175000017500000001171312210154276020043 0ustar takakitakakiimport optparse import sys import textwrap from pyramid.compat import url_unquote from pyramid.request import Request from pyramid.paster import get_app from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PRequestCommand(argv, quiet) return command.run() class PRequestCommand(object): description = """\ Run a request for the described application. This command makes an artifical request to a web application that uses a PasteDeploy (.ini) configuration file for the server and application. Use "prequest config.ini /path" to request "/path". Use "prequest --method=POST config.ini /path < data" to do a POST with the given request body. If the path is relative (doesn't begin with "/") it is interpreted as relative to "/". The path passed to this script should be URL-quoted. The path can be succeeded with a query string (e.g. `/path?a=1&=b2'). The variable "environ['paste.command_request']" will be set to "True" in the request's WSGI environment, so your application can distinguish these calls from normal requests. """ usage = "usage: %prog config_uri path_info [args/options]" parser = optparse.OptionParser( usage=usage, description=textwrap.dedent(description) ) parser.add_option( '-n', '--app-name', dest='app_name', metavar= 'NAME', help="Load the named application from the config file (default 'main')", type="string", ) parser.add_option( '--header', dest='headers', metavar='NAME:VALUE', type='string', action='append', help="Header to add to request (you can use this option multiple times)" ) parser.add_option( '-d', '--display-headers', dest='display_headers', action='store_true', help='Display status and headers before the response body' ) parser.add_option( '-m', '--method', dest='method', choices=['GET', 'HEAD', 'POST', 'DELETE'], type='choice', help='Request method type (GET, POST, DELETE)', ) get_app = staticmethod(get_app) stdin = sys.stdin def __init__(self, argv, quiet=False): self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def out(self, msg): # pragma: no cover if not self.quiet: print(msg) def run(self): if not len(self.args) >= 2: self.out('You must provide at least two arguments') return 2 app_spec = self.args[0] path = self.args[1] if not path.startswith('/'): path = '/' + path try: path, qs = path.split('?', 1) except ValueError: qs = '' path = url_unquote(path) headers = {} if self.options.headers: for item in self.options.headers: if ':' not in item: self.out( "Bad --header=%s option, value must be in the form " "'name:value'" % item) return 2 name, value = item.split(':', 1) headers[name] = value.strip() app = self.get_app(app_spec, self.options.app_name, options=parse_vars(self.args[2:])) request_method = (self.options.method or 'GET').upper() environ = { 'REQUEST_METHOD': request_method, 'SCRIPT_NAME': '', # may be empty if app is at the root 'PATH_INFO': path, 'SERVER_NAME': 'localhost', # always mandatory 'SERVER_PORT': '80', # always mandatory 'SERVER_PROTOCOL': 'HTTP/1.0', 'CONTENT_TYPE': 'text/plain', 'REMOTE_ADDR':'127.0.0.1', 'wsgi.run_once': True, 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.errors': sys.stderr, 'wsgi.url_scheme': 'http', 'wsgi.version': (1, 0), 'QUERY_STRING': qs, 'HTTP_ACCEPT': 'text/plain;q=1.0, */*;q=0.1', 'paste.command_request': True, } if request_method == 'POST': environ['wsgi.input'] = self.stdin environ['CONTENT_LENGTH'] = '-1' for name, value in headers.items(): if name.lower() == 'content-type': name = 'CONTENT_TYPE' else: name = 'HTTP_'+name.upper().replace('-', '_') environ[name] = value request = Request.blank(path, environ=environ) response = request.get_response(app) if self.options.display_headers: self.out(response.status) for name, value in response.headerlist: self.out('%s: %s' % (name, value)) if response.charset: self.out(response.ubody) else: self.out(response.body) return 0 pyramid-1.4.5/pyramid/scripts/common.py0000664000175000017500000000221112203712502017446 0ustar takakitakakiimport os from pyramid.compat import configparser from logging.config import fileConfig def parse_vars(args): """ Given variables like ``['a=b', 'c=d']`` turns it into ``{'a': 'b', 'c': 'd'}`` """ result = {} for arg in args: if '=' not in arg: raise ValueError( 'Variable assignment %r invalid (no "=")' % arg) name, value = arg.split('=', 1) result[name] = value return result def logging_file_config(config_file, fileConfig=fileConfig, configparser=configparser): """ Setup logging via the logging module's fileConfig function with the specified ``config_file``, if applicable. ConfigParser defaults are specified for the special ``__file__`` and ``here`` variables, similar to PasteDeploy config loading. """ parser = configparser.ConfigParser() parser.read([config_file]) if parser.has_section('loggers'): config_file = os.path.abspath(config_file) return fileConfig( config_file, dict(__file__=config_file, here=os.path.dirname(config_file)) ) pyramid-1.4.5/pyramid/scripts/ptweens.py0000664000175000017500000000615412203712502017655 0ustar takakitakakiimport optparse import sys import textwrap from pyramid.interfaces import ITweens from pyramid.tweens import MAIN from pyramid.tweens import INGRESS from pyramid.paster import bootstrap from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PTweensCommand(argv, quiet) return command.run() class PTweensCommand(object): usage = '%prog config_uri' description = """\ Print all implicit and explicit tween objects used by a Pyramid application. The handler output includes whether the system is using an explicit tweens ordering (will be true when the "pyramid.tweens" deployment setting is used) or an implicit tweens ordering (will be true when the "pyramid.tweens" deployment setting is *not* used). This command accepts one positional argument named "config_uri" which 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. Example: "ptweens myapp.ini#main". """ parser = optparse.OptionParser( usage, description=textwrap.dedent(description), ) stdout = sys.stdout bootstrap = (bootstrap,) # testing def __init__(self, argv, quiet=False): self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def _get_tweens(self, registry): from pyramid.config import Configurator config = Configurator(registry = registry) return config.registry.queryUtility(ITweens) def out(self, msg): # pragma: no cover if not self.quiet: print(msg) def show_chain(self, chain): fmt = '%-10s %-65s' self.out(fmt % ('Position', 'Name')) self.out(fmt % ('-'*len('Position'), '-'*len('Name'))) self.out(fmt % ('-', INGRESS)) for pos, (name, _) in enumerate(chain): self.out(fmt % (pos, name)) self.out(fmt % ('-', MAIN)) def run(self): if not self.args: self.out('Requires a config file argument') return 2 config_uri = self.args[0] env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) registry = env['registry'] tweens = self._get_tweens(registry) if tweens is not None: explicit = tweens.explicit if explicit: self.out('"pyramid.tweens" config value set ' '(explicitly ordered tweens used)') self.out('') self.out('Explicit Tween Chain (used)') self.out('') self.show_chain(tweens.explicit) self.out('') self.out('Implicit Tween Chain (not used)') self.out('') self.show_chain(tweens.implicit()) else: self.out('"pyramid.tweens" config value NOT set ' '(implicitly ordered tweens used)') self.out('') self.out('Implicit Tween Chain') self.out('') self.show_chain(tweens.implicit()) return 0 pyramid-1.4.5/pyramid/scripts/pshell.py0000664000175000017500000001646212203712502017462 0ustar takakitakakifrom code import interact import optparse import sys import textwrap from pyramid.compat import configparser from pyramid.util import DottedNameResolver from pyramid.paster import bootstrap from pyramid.paster import setup_logging from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PShellCommand(argv, quiet) return command.run() class PShellCommand(object): usage = '%prog config_uri' description = """\ Open an interactive shell with a Pyramid app loaded. This command accepts one positional argument named "config_uri" which specifies the PasteDeploy config file to use for the interactive shell. The format is "inifile#name". If the name is left off, the Pyramid default application will be assumed. Example: "pshell myapp.ini#main" If you do not point the loader directly at the section of the ini file containing your Pyramid application, the command will attempt to find the app for you. If you are loading a pipeline that contains more than one Pyramid application within it, the loader will use the last one. """ bootstrap = (bootstrap,) # for testing parser = optparse.OptionParser( usage, description=textwrap.dedent(description) ) parser.add_option('-p', '--python-shell', action='store', type='string', dest='python_shell', default='', help='ipython | bpython | python') parser.add_option('--setup', dest='setup', help=("A callable that will be passed the environment " "before it is made available to the shell. This " "option will override the 'setup' key in the " "[pshell] ini section.")) ConfigParser = configparser.ConfigParser # testing loaded_objects = {} object_help = {} setup = None def __init__(self, argv, quiet=False): self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def pshell_file_config(self, filename): config = self.ConfigParser() config.read(filename) try: items = config.items('pshell') except configparser.NoSectionError: return resolver = DottedNameResolver(None) self.loaded_objects = {} self.object_help = {} self.setup = None for k, v in items: if k == 'setup': self.setup = v else: self.loaded_objects[k] = resolver.maybe_resolve(v) self.object_help[k] = v def out(self, msg): # pragma: no cover if not self.quiet: print(msg) def run(self, shell=None): if not self.args: self.out('Requires a config file argument') return 2 config_uri = self.args[0] config_file = config_uri.split('#', 1)[0] setup_logging(config_file) self.pshell_file_config(config_file) # bootstrap the environ env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) # remove the closer from the env closer = env.pop('closer') # setup help text for default environment env_help = dict(env) env_help['app'] = 'The WSGI application.' env_help['root'] = 'Root of the default resource tree.' env_help['registry'] = 'Active Pyramid registry.' env_help['request'] = 'Active request object.' env_help['root_factory'] = ( 'Default root factory used to create `root`.') # override use_script with command-line options if self.options.setup: self.setup = self.options.setup if self.setup: # store the env before muddling it with the script orig_env = env.copy() # call the setup callable resolver = DottedNameResolver(None) setup = resolver.maybe_resolve(self.setup) setup(env) # remove any objects from default help that were overidden for k, v in env.items(): if k not in orig_env or env[k] != orig_env[k]: env_help[k] = v # load the pshell section of the ini file env.update(self.loaded_objects) # eliminate duplicates from env, allowing custom vars to override for k in self.loaded_objects: if k in env_help: del env_help[k] # generate help text help = '' if env_help: help += 'Environment:' for var in sorted(env_help.keys()): help += '\n %-12s %s' % (var, env_help[var]) if self.object_help: help += '\n\nCustom Variables:' for var in sorted(self.object_help.keys()): help += '\n %-12s %s' % (var, self.object_help[var]) if shell is None: shell = self.make_shell() try: shell(env, help) finally: closer() def make_shell(self): shell = None user_shell = self.options.python_shell.lower() if not user_shell: shell = self.make_ipython_v0_11_shell() if shell is None: shell = self.make_ipython_v0_10_shell() if shell is None: shell = self.make_bpython_shell() elif user_shell == 'ipython': shell = self.make_ipython_v0_11_shell() if shell is None: shell = self.make_ipython_v0_10_shell() elif user_shell == 'bpython': shell = self.make_bpython_shell() if shell is None: shell = self.make_default_shell() return shell def make_default_shell(self, interact=interact): def shell(env, help): cprt = 'Type "help" for more information.' banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) banner += '\n\n' + help + '\n' interact(banner, local=env) return shell def make_bpython_shell(self, BPShell=None): if BPShell is None: # pragma: no cover try: from bpython import embed BPShell = embed except ImportError: return None def shell(env, help): BPShell(locals_=env, banner=help + '\n') return shell def make_ipython_v0_11_shell(self, IPShellFactory=None): if IPShellFactory is None: # pragma: no cover try: from IPython.frontend.terminal.embed import ( InteractiveShellEmbed) IPShellFactory = InteractiveShellEmbed except ImportError: return None def shell(env, help): IPShell = IPShellFactory(banner2=help + '\n', user_ns=env) IPShell() return shell def make_ipython_v0_10_shell(self, IPShellFactory=None): if IPShellFactory is None: # pragma: no cover try: from IPython.Shell import IPShellEmbed IPShellFactory = IPShellEmbed except ImportError: return None def shell(env, help): IPShell = IPShellFactory(argv=[], user_ns=env) IPShell.set_banner(IPShell.IP.BANNER + '\n' + help + '\n') IPShell() return shell pyramid-1.4.5/pyramid/scripts/proutes.py0000664000175000017500000000577112203712502017675 0ustar takakitakakiimport optparse import sys import textwrap from pyramid.paster import bootstrap from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PRoutesCommand(argv, quiet) return command.run() class PRoutesCommand(object): description = """\ Print all URL dispatch routes used by a Pyramid application in the order in which they are evaluated. Each route includes the name of the route, the pattern of the route, and the view callable which will be invoked when the route is matched. This command accepts one positional argument named 'config_uri'. It 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. Example: 'proutes myapp.ini'. """ bootstrap = (bootstrap,) stdout = sys.stdout usage = '%prog config_uri' parser = optparse.OptionParser( usage, description=textwrap.dedent(description) ) def __init__(self, argv, quiet=False): self.options, self.args = self.parser.parse_args(argv[1:]) self.quiet = quiet def _get_mapper(self, registry): from pyramid.config import Configurator config = Configurator(registry = registry) return config.get_routes_mapper() def out(self, msg): # pragma: no cover if not self.quiet: print(msg) def run(self, quiet=False): if not self.args: self.out('requires a config file argument') return 2 from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IView from zope.interface import Interface config_uri = self.args[0] env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) registry = env['registry'] mapper = self._get_mapper(registry) if mapper is not None: routes = mapper.get_routes() fmt = '%-15s %-30s %-25s' if not routes: return 0 self.out(fmt % ('Name', 'Pattern', 'View')) self.out( fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View'))) for route in routes: pattern = route.pattern if not pattern.startswith('/'): pattern = '/' + pattern request_iface = registry.queryUtility(IRouteRequest, name=route.name) view_callable = None if (request_iface is None) or (route.factory is not None): self.out(fmt % (route.name, pattern, '')) else: view_callable = registry.adapters.lookup( (IViewClassifier, request_iface, Interface), IView, name='', default=None) self.out(fmt % (route.name, pattern, view_callable)) return 0 pyramid-1.4.5/pyramid/scripts/pcreate.py0000664000175000017500000001136112203712502017607 0ustar takakitakaki# (c) 2005 Ian Bicking and contributors; written for Paste # (http://pythonpaste.org) Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php import optparse import os import os.path import pkg_resources import re import sys _bad_chars_re = re.compile('[^a-zA-Z0-9_]') def main(argv=sys.argv, quiet=False): command = PCreateCommand(argv, quiet) return command.run() class PCreateCommand(object): verbosity = 1 # required description = "Render Pyramid scaffolding to an output directory" usage = "usage: %prog [options] output_directory" parser = optparse.OptionParser(usage, description=description) parser.add_option('-s', '--scaffold', dest='scaffold_name', action='append', help=("Add a scaffold to the create process " "(multiple -s args accepted)")) parser.add_option('-t', '--template', dest='scaffold_name', action='append', help=('A backwards compatibility alias for ' '-s/--scaffold. Add a scaffold to the ' 'create process (multiple -t args accepted)')) parser.add_option('-l', '--list', dest='list', action='store_true', help="List all available scaffold names") parser.add_option('--list-templates', dest='list', action='store_true', help=("A backwards compatibility alias for -l/--list. " "List all available scaffold names.")) parser.add_option('--simulate', dest='simulate', action='store_true', help='Simulate but do no work') parser.add_option('--overwrite', dest='overwrite', action='store_true', help='Always overwrite') parser.add_option('--interactive', dest='interactive', action='store_true', help='When a file would be overwritten, interrogate') def __init__(self, argv, quiet=False): self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) self.scaffolds = self.all_scaffolds() def run(self): if self.options.list: return self.show_scaffolds() if not self.options.scaffold_name: self.out('You must provide at least one scaffold name') return 2 if not self.args: self.out('You must provide a project name') return 2 available = [x.name for x in self.scaffolds] diff = set(self.options.scaffold_name).difference(available) if diff: self.out('Unavailable scaffolds: %s' % list(diff)) return 2 return self.render_scaffolds() def render_scaffolds(self): options = self.options args = self.args project_name = os.path.basename(args[0]) output_dir = os.path.abspath(os.path.normpath(args[0])) pkg_name = _bad_chars_re.sub('', project_name.lower()) safe_name = pkg_resources.safe_name(project_name) egg_name = pkg_resources.to_filename(safe_name) vars = { 'project': project_name, 'package': pkg_name, 'egg': egg_name, } for scaffold_name in options.scaffold_name: for scaffold in self.scaffolds: if scaffold.name == scaffold_name: scaffold.run(self, output_dir, vars) return 0 def show_scaffolds(self): scaffolds = sorted(self.scaffolds, key=lambda x: x.name) if scaffolds: max_name = max([len(t.name) for t in scaffolds]) self.out('Available scaffolds:') for scaffold in scaffolds: self.out(' %s:%s %s' % ( scaffold.name, ' '*(max_name-len(scaffold.name)), scaffold.summary)) else: self.out('No scaffolds available') return 0 def all_scaffolds(self): scaffolds = [] eps = list(pkg_resources.iter_entry_points('pyramid.scaffold')) for entry in eps: try: scaffold_class = entry.load() scaffold = scaffold_class(entry.name) scaffolds.append(scaffold) except Exception as e: # pragma: no cover self.out('Warning: could not load entry point %s (%s: %s)' % ( entry.name, e.__class__.__name__, e)) return scaffolds def out(self, msg): # pragma: no cover if not self.quiet: print(msg) pyramid-1.4.5/pyramid/scripts/pviews.py0000664000175000017500000002456512203712502017513 0ustar takakitakakiimport optparse import sys import textwrap from pyramid.interfaces import IMultiView from pyramid.paster import bootstrap from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PViewsCommand(argv, quiet) return command.run() class PViewsCommand(object): usage = '%prog config_uri url' description = """\ Print, for a given URL, the views that might match. Underneath each potentially matching route, list the predicates required. Underneath each route+predicate set, print each view that might match and its predicates. This command accepts two positional arguments: '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. 'url' specifies the path info portion of a URL that will be used to find matching views. Example: 'proutes myapp.ini#main /url' """ stdout = sys.stdout parser = optparse.OptionParser( usage, description=textwrap.dedent(description) ) bootstrap = (bootstrap,) # testing def __init__(self, argv, quiet=False): self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def out(self, msg): # pragma: no cover if not self.quiet: print(msg) def _find_multi_routes(self, mapper, request): infos = [] path = request.environ['PATH_INFO'] # find all routes that match path, regardless of predicates for route in mapper.get_routes(): match = route.match(path) if match is not None: info = {'match':match, 'route':route} infos.append(info) return infos def _find_view(self, url, registry): """ Accept ``url`` and ``registry``; create a :term:`request` and find a :app:`Pyramid` view based on introspection of :term:`view configuration` within the application registry; return the view. """ from zope.interface import providedBy from zope.interface import implementer from pyramid.interfaces import IRequest from pyramid.interfaces import IRootFactory from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IRequestFactory from pyramid.interfaces import IRoutesMapper from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import ITraverser from pyramid.request import Request from pyramid.traversal import DefaultRootFactory from pyramid.traversal import ResourceTreeTraverser q = registry.queryUtility root_factory = q(IRootFactory, default=DefaultRootFactory) routes_mapper = q(IRoutesMapper) request_factory = q(IRequestFactory, default=Request) adapters = registry.adapters request = None @implementer(IMultiView) class RoutesMultiView(object): def __init__(self, infos, context_iface, root_factory, request): self.views = [] for info in infos: match, route = info['match'], info['route'] if route is not None: request_iface = registry.queryUtility( IRouteRequest, name=route.name, default=IRequest) view = adapters.lookup( (IViewClassifier, request_iface, context_iface), IView, name='', default=None) if view is None: continue view.__request_attrs__ = {} view.__request_attrs__['matchdict'] = match view.__request_attrs__['matched_route'] = route root_factory = route.factory or root_factory root = root_factory(request) traverser = adapters.queryAdapter(root, ITraverser) if traverser is None: traverser = ResourceTreeTraverser(root) tdict = traverser(request) view.__request_attrs__.update(tdict) if not hasattr(view, '__view_attr__'): view.__view_attr__ = '' self.views.append((None, view, None)) # create the request environ = { 'wsgi.url_scheme':'http', 'SERVER_NAME':'localhost', 'SERVER_PORT':'8080', 'REQUEST_METHOD':'GET', 'PATH_INFO':url, } request = request_factory(environ) context = None routes_multiview = None attrs = request.__dict__ attrs['registry'] = registry request_iface = IRequest # find the root object if routes_mapper is not None: infos = self._find_multi_routes(routes_mapper, request) if len(infos) == 1: info = infos[0] match, route = info['match'], info['route'] if route is not None: attrs['matchdict'] = match attrs['matched_route'] = route request.environ['bfg.routes.matchdict'] = match request_iface = registry.queryUtility( IRouteRequest, name=route.name, default=IRequest) root_factory = route.factory or root_factory if len(infos) > 1: routes_multiview = infos root = root_factory(request) attrs['root'] = root # find a context traverser = adapters.queryAdapter(root, ITraverser) if traverser is None: traverser = ResourceTreeTraverser(root) tdict = traverser(request) context, view_name, subpath, traversed, vroot, vroot_path =( tdict['context'], tdict['view_name'], tdict['subpath'], tdict['traversed'], tdict['virtual_root'], tdict['virtual_root_path']) attrs.update(tdict) # find a view callable context_iface = providedBy(context) if routes_multiview is None: view = adapters.lookup( (IViewClassifier, request_iface, context_iface), IView, name=view_name, default=None) else: view = RoutesMultiView(infos, context_iface, root_factory, request) # routes are not registered with a view name if view is None: view = adapters.lookup( (IViewClassifier, request_iface, context_iface), IView, name='', default=None) # we don't want a multiview here if IMultiView.providedBy(view): view = None if view is not None: view.__request_attrs__ = attrs return view def output_route_attrs(self, attrs, indent): route = attrs['matched_route'] self.out("%sroute name: %s" % (indent, route.name)) self.out("%sroute pattern: %s" % (indent, route.pattern)) self.out("%sroute path: %s" % (indent, route.path)) self.out("%ssubpath: %s" % (indent, '/'.join(attrs['subpath']))) predicates = ', '.join([p.text() for p in route.predicates]) if predicates != '': self.out("%sroute predicates (%s)" % (indent, predicates)) def output_view_info(self, view_wrapper, level=1): indent = " " * level name = getattr(view_wrapper, '__name__', '') module = getattr(view_wrapper, '__module__', '') attr = getattr(view_wrapper, '__view_attr__', None) request_attrs = getattr(view_wrapper, '__request_attrs__', {}) if attr is not None: view_callable = "%s.%s.%s" % (module, name, attr) else: attr = view_wrapper.__class__.__name__ if attr == 'function': attr = name view_callable = "%s.%s" % (module, attr) self.out('') if 'matched_route' in request_attrs: self.out("%sRoute:" % indent) self.out("%s------" % indent) self.output_route_attrs(request_attrs, indent) permission = getattr(view_wrapper, '__permission__', None) if not IMultiView.providedBy(view_wrapper): # single view for this route, so repeat call without route data del request_attrs['matched_route'] self.output_view_info(view_wrapper, level+1) else: self.out("%sView:" % indent) self.out("%s-----" % indent) self.out("%s%s" % (indent, view_callable)) permission = getattr(view_wrapper, '__permission__', None) if permission is not None: self.out("%srequired permission = %s" % (indent, permission)) predicates = getattr(view_wrapper, '__predicates__', None) if predicates is not None: predicate_text = ', '.join([p.text() for p in predicates]) self.out("%sview predicates (%s)" % (indent, predicate_text)) def run(self): if len(self.args) < 2: self.out('Command requires a config file arg and a url arg') return 2 config_uri = self.args[0] url = self.args[1] if not url.startswith('/'): url = '/%s' % url env = self.bootstrap[0](config_uri, options=parse_vars(self.args[2:])) registry = env['registry'] view = self._find_view(url, registry) self.out('') self.out("URL = %s" % url) self.out('') if view is not None: self.out(" context: %s" % view.__request_attrs__['context']) self.out(" view name: %s" % view.__request_attrs__['view_name']) if IMultiView.providedBy(view): for dummy, view_wrapper, dummy in view.views: self.output_view_info(view_wrapper) if IMultiView.providedBy(view_wrapper): for dummy, mv_view_wrapper, dummy in view_wrapper.views: self.output_view_info(mv_view_wrapper, level=2) else: if view is not None: self.output_view_info(view) else: self.out(" Not found.") self.out('') return 0 pyramid-1.4.5/pyramid/scripts/pserve.py0000664000175000017500000007574012210154301017476 0ustar takakitakaki# (c) 2005 Ian Bicking and contributors; written for Paste # (http://pythonpaste.org) Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php # # For discussion of daemonizing: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 # # Code taken also from QP: http://www.mems-exchange.org/software/qp/ From # lib/site.py import atexit import ctypes import errno import logging import optparse import os import re import subprocess import sys import textwrap import threading import time import traceback from paste.deploy import loadserver from paste.deploy import loadapp from pyramid.compat import WIN from pyramid.paster import setup_logging from pyramid.scripts.common import parse_vars MAXFD = 1024 if WIN and not hasattr(os, 'kill'): # pragma: no cover # py 2.6 on windows def kill(pid, sig=None): """kill function for Win32""" # signal is ignored, semibogus raise message kernel32 = ctypes.windll.kernel32 handle = kernel32.OpenProcess(1, 0, pid) if (0 == kernel32.TerminateProcess(handle, 0)): raise OSError('No such process %s' % pid) else: kill = os.kill def main(argv=sys.argv, quiet=False): command = PServeCommand(argv, quiet=quiet) return command.run() class DaemonizeException(Exception): pass class PServeCommand(object): usage = '%prog config_uri [start|stop|restart|status] [var=value]' description = """\ This command serves a web application that uses a PasteDeploy configuration file for the server and application. If start/stop/restart is given, then --daemon is implied, and it will start (normal operation), stop (--stop-daemon), or do both. You can also include variable assignments like 'http_port=8080' and then use %(http_port)s in your config files. """ verbose = 1 parser = optparse.OptionParser( usage, description=textwrap.dedent(description) ) parser.add_option( '-n', '--app-name', dest='app_name', metavar='NAME', help="Load the named application (default main)") parser.add_option( '-s', '--server', dest='server', metavar='SERVER_TYPE', help="Use the named server.") parser.add_option( '--server-name', dest='server_name', metavar='SECTION_NAME', help=("Use the named server as defined in the configuration file " "(default: main)")) if hasattr(os, 'fork'): parser.add_option( '--daemon', dest="daemon", action="store_true", help="Run in daemon (background) mode") parser.add_option( '--pid-file', dest='pid_file', metavar='FILENAME', help=("Save PID to file (default to pyramid.pid if running in " "daemon mode)")) parser.add_option( '--log-file', dest='log_file', metavar='LOG_FILE', help="Save output to the given log file (redirects stdout)") parser.add_option( '--reload', dest='reload', action='store_true', help="Use auto-restart file monitor") parser.add_option( '--reload-interval', dest='reload_interval', default=1, help=("Seconds between checking files (low number can cause " "significant CPU usage)")) parser.add_option( '--monitor-restart', dest='monitor_restart', action='store_true', help="Auto-restart server if it dies") parser.add_option( '--status', action='store_true', dest='show_status', help="Show the status of the (presumably daemonized) server") if hasattr(os, 'setuid'): # I don't think these are available on Windows parser.add_option( '--user', dest='set_user', metavar="USERNAME", help="Set the user (usually only possible when run as root)") parser.add_option( '--group', dest='set_group', metavar="GROUP", help="Set the group (usually only possible when run as root)") parser.add_option( '--stop-daemon', dest='stop_daemon', action='store_true', help=('Stop a daemonized server (given a PID file, or default ' 'pyramid.pid file)')) _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) default_verbosity = 1 _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN' _monitor_environ_key = 'PASTE_MONITOR_SHOULD_RUN' possible_subcommands = ('start', 'stop', 'restart', 'status') def __init__(self, argv, quiet=False): self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def out(self, msg): # pragma: no cover if not self.quiet: print(msg) def get_options(self): if (len(self.args) > 1 and self.args[1] in self.possible_subcommands): restvars = self.args[2:] else: restvars = self.args[1:] return parse_vars(restvars) def run(self): # pragma: no cover if self.options.stop_daemon: return self.stop_daemon() if not hasattr(self.options, 'set_user'): # Windows case: self.options.set_user = self.options.set_group = None # @@: Is this the right stage to set the user at? self.change_user_group( self.options.set_user, self.options.set_group) if not self.args: self.out('You must give a config file') return 2 app_spec = self.args[0] if (len(self.args) > 1 and self.args[1] in self.possible_subcommands): cmd = self.args[1] else: cmd = None if self.options.reload: if os.environ.get(self._reloader_environ_key): if self.verbose > 1: self.out('Running reloading file monitor') install_reloader(int(self.options.reload_interval), [app_spec]) # if self.requires_config_file: # watch_file(self.args[0]) else: return self.restart_with_reloader() if cmd not in (None, 'start', 'stop', 'restart', 'status'): self.out( 'Error: must give start|stop|restart (not %s)' % cmd) return 2 if cmd == 'status' or self.options.show_status: return self.show_status() if cmd == 'restart' or cmd == 'stop': result = self.stop_daemon() if result: if cmd == 'restart': self.out("Could not stop daemon; aborting") else: self.out("Could not stop daemon") return result if cmd == 'stop': return result self.options.daemon = True if cmd == 'start': self.options.daemon = True app_name = self.options.app_name vars = self.get_options() if not self._scheme_re.search(app_spec): app_spec = 'config:' + app_spec server_name = self.options.server_name if self.options.server: server_spec = 'egg:pyramid' assert server_name is None server_name = self.options.server else: server_spec = app_spec base = os.getcwd() if getattr(self.options, 'daemon', False): if not self.options.pid_file: self.options.pid_file = 'pyramid.pid' if not self.options.log_file: self.options.log_file = 'pyramid.log' # Ensure the log file is writeable if self.options.log_file: try: writeable_log_file = open(self.options.log_file, 'a') except IOError as ioe: msg = 'Error: Unable to write to log file: %s' % ioe raise ValueError(msg) writeable_log_file.close() # Ensure the pid file is writeable if self.options.pid_file: try: writeable_pid_file = open(self.options.pid_file, 'a') except IOError as ioe: msg = 'Error: Unable to write to pid file: %s' % ioe raise ValueError(msg) writeable_pid_file.close() if getattr(self.options, 'daemon', False): try: self.daemonize() except DaemonizeException as ex: if self.verbose > 0: self.out(str(ex)) return 2 if (self.options.monitor_restart and not os.environ.get(self._monitor_environ_key)): return self.restart_with_monitor() if self.options.pid_file: self.record_pid(self.options.pid_file) if self.options.log_file: stdout_log = LazyWriter(self.options.log_file, 'a') sys.stdout = stdout_log sys.stderr = stdout_log logging.basicConfig(stream=stdout_log) log_fn = app_spec if log_fn.startswith('config:'): log_fn = app_spec[len('config:'):] elif log_fn.startswith('egg:'): log_fn = None if log_fn: log_fn = os.path.join(base, log_fn) setup_logging(log_fn) server = self.loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars) app = self.loadapp(app_spec, name=app_name, relative_to=base, global_conf=vars) if self.verbose > 0: if hasattr(os, 'getpid'): msg = 'Starting server in PID %i.' % os.getpid() else: msg = 'Starting server.' self.out(msg) def serve(): try: server(app) except (SystemExit, KeyboardInterrupt) as e: if self.verbose > 1: raise if str(e): msg = ' ' + str(e) else: msg = '' self.out('Exiting%s (-v to see traceback)' % msg) serve() def loadapp(self, app_spec, name, relative_to, **kw): # pragma: no cover return loadapp(app_spec, name=name, relative_to=relative_to, **kw) def loadserver(self, server_spec, name, relative_to, **kw):# pragma:no cover return loadserver( server_spec, name=name, relative_to=relative_to, **kw) def quote_first_command_arg(self, arg): # pragma: no cover """ There's a bug in Windows when running an executable that's located inside a path with a space in it. This method handles that case, or on non-Windows systems or an executable with no spaces, it just leaves well enough alone. """ if (sys.platform != 'win32' or ' ' not in arg): # Problem does not apply: return arg try: import win32api except ImportError: raise ValueError( "The executable %r contains a space, and in order to " "handle this issue you must have the win32api module " "installed" % arg) arg = win32api.GetShortPathName(arg) return arg def daemonize(self): # pragma: no cover pid = live_pidfile(self.options.pid_file) if pid: raise DaemonizeException( "Daemon is already running (PID: %s from PID file %s)" % (pid, self.options.pid_file)) if self.verbose > 0: self.out('Entering daemon mode') pid = os.fork() if pid: # The forked process also has a handle on resources, so we # *don't* want proper termination of the process, we just # want to exit quick (which os._exit() does) os._exit(0) # Make this the session leader os.setsid() # Fork again for good measure! pid = os.fork() if pid: os._exit(0) # @@: Should we set the umask and cwd now? import resource # Resource usage information. maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if (maxfd == resource.RLIM_INFINITY): maxfd = MAXFD # Iterate through and close all file descriptors. for fd in range(0, maxfd): try: os.close(fd) except OSError: # ERROR, fd wasn't open to begin with (ignored) pass if (hasattr(os, "devnull")): REDIRECT_TO = os.devnull else: REDIRECT_TO = "/dev/null" os.open(REDIRECT_TO, os.O_RDWR) # standard input (0) # Duplicate standard input to standard output and standard error. os.dup2(0, 1) # standard output (1) os.dup2(0, 2) # standard error (2) def _remove_pid_file(self, written_pid, filename, verbosity): current_pid = os.getpid() if written_pid != current_pid: # A forked process must be exiting, not the process that # wrote the PID file return if not os.path.exists(filename): return with open(filename) as f: content = f.read().strip() try: pid_in_file = int(content) except ValueError: pass else: if pid_in_file != current_pid: msg = "PID file %s contains %s, not expected PID %s" self.out(msg % (filename, pid_in_file, current_pid)) return if verbosity > 0: self.out("Removing PID file %s" % filename) try: os.unlink(filename) return except OSError as e: # Record, but don't give traceback self.out("Cannot remove PID file: (%s)" % e) # well, at least lets not leave the invalid PID around... try: with open(filename, 'w') as f: f.write('') except OSError as e: self.out('Stale PID left in file: %s (%s)' % (filename, e)) else: self.out('Stale PID removed') def record_pid(self, pid_file): pid = os.getpid() if self.verbose > 1: self.out('Writing PID %s to %s' % (pid, pid_file)) with open(pid_file, 'w') as f: f.write(str(pid)) atexit.register(self._remove_pid_file, pid, pid_file, self.verbose) def stop_daemon(self): # pragma: no cover pid_file = self.options.pid_file or 'pyramid.pid' if not os.path.exists(pid_file): self.out('No PID file exists in %s' % pid_file) return 1 pid = read_pidfile(pid_file) if not pid: self.out("Not a valid PID file in %s" % pid_file) return 1 pid = live_pidfile(pid_file) if not pid: self.out("PID in %s is not valid (deleting)" % pid_file) try: os.unlink(pid_file) except (OSError, IOError) as e: self.out("Could not delete: %s" % e) return 2 return 1 for j in range(10): if not live_pidfile(pid_file): break import signal kill(pid, signal.SIGTERM) time.sleep(1) else: self.out("failed to kill web process %s" % pid) return 3 if os.path.exists(pid_file): os.unlink(pid_file) return 0 def show_status(self): # pragma: no cover pid_file = self.options.pid_file or 'pyramid.pid' if not os.path.exists(pid_file): self.out('No PID file %s' % pid_file) return 1 pid = read_pidfile(pid_file) if not pid: self.out('No PID in file %s' % pid_file) return 1 pid = live_pidfile(pid_file) if not pid: self.out('PID %s in %s is not running' % (pid, pid_file)) return 1 self.out('Server running in PID %s' % pid) return 0 def restart_with_reloader(self): # pragma: no cover self.restart_with_monitor(reloader=True) def restart_with_monitor(self, reloader=False): # pragma: no cover if self.verbose > 0: if reloader: self.out('Starting subprocess with file monitor') else: self.out('Starting subprocess with monitor parent') while 1: args = [self.quote_first_command_arg(sys.executable)] + sys.argv new_environ = os.environ.copy() if reloader: new_environ[self._reloader_environ_key] = 'true' else: new_environ[self._monitor_environ_key] = 'true' proc = None try: try: _turn_sigterm_into_systemexit() proc = subprocess.Popen(args, env=new_environ) exit_code = proc.wait() proc = None except KeyboardInterrupt: self.out('^C caught in monitor process') if self.verbose > 1: raise return 1 finally: if proc is not None: import signal try: kill(proc.pid, signal.SIGTERM) except (OSError, IOError): pass if reloader: # Reloader always exits with code 3; but if we are # a monitor, any exit code will restart if exit_code != 3: return exit_code if self.verbose > 0: self.out('%s %s %s' % ('-' * 20, 'Restarting', '-' * 20)) def change_user_group(self, user, group): # pragma: no cover if not user and not group: return import pwd, grp uid = gid = None if group: try: gid = int(group) group = grp.getgrgid(gid).gr_name except ValueError: import grp try: entry = grp.getgrnam(group) except KeyError: raise ValueError( "Bad group: %r; no such group exists" % group) gid = entry.gr_gid try: uid = int(user) user = pwd.getpwuid(uid).pw_name except ValueError: try: entry = pwd.getpwnam(user) except KeyError: raise ValueError( "Bad username: %r; no such user exists" % user) if not gid: gid = entry.pw_gid uid = entry.pw_uid if self.verbose > 0: self.out('Changing user to %s:%s (%s:%s)' % ( user, group or '(unknown)', uid, gid)) if gid: os.setgid(gid) if uid: os.setuid(uid) class LazyWriter(object): """ File-like object that opens a file lazily when it is first written to. """ def __init__(self, filename, mode='w'): self.filename = filename self.fileobj = None self.lock = threading.Lock() self.mode = mode def open(self): if self.fileobj is None: with self.lock: self.fileobj = open(self.filename, self.mode) return self.fileobj def close(self): fileobj = self.fileobj if fileobj is not None: fileobj.close() def __del__(self): self.close() def write(self, text): fileobj = self.open() fileobj.write(text) fileobj.flush() def writelines(self, text): fileobj = self.open() fileobj.writelines(text) fileobj.flush() def flush(self): self.open().flush() def live_pidfile(pidfile): # pragma: no cover """(pidfile:str) -> int | None Returns an int found in the named file, if there is one, and if there is a running process with that process id. Return None if no such process exists. """ pid = read_pidfile(pidfile) if pid: try: kill(int(pid), 0) return pid except OSError as e: if e.errno == errno.EPERM: return pid return None def read_pidfile(filename): if os.path.exists(filename): try: with open(filename) as f: content = f.read() return int(content.strip()) except (ValueError, IOError): return None else: return None def ensure_port_cleanup( bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover """ This makes sure any open ports are closed. Does this by connecting to them until they give connection refused. Servers should call like:: ensure_port_cleanup([80, 443]) """ atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, sleeptime=sleeptime) def _cleanup_ports( bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover # Wait for the server to bind to the port. import socket import errno for bound_address in bound_addresses: for attempt in range(maxtries): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect(bound_address) except socket.error as e: if e.args[0] != errno.ECONNREFUSED: raise break else: time.sleep(sleeptime) else: raise SystemExit('Timeout waiting for port.') sock.close() def _turn_sigterm_into_systemexit(): # pragma: no cover """ Attempts to turn a SIGTERM exception into a SystemExit exception. """ try: import signal except ImportError: return def handle_term(signo, frame): raise SystemExit signal.signal(signal.SIGTERM, handle_term) def install_reloader(poll_interval=1, extra_files=None): # pragma: no cover """ Install the reloading monitor. On some platforms server threads may not terminate when the main thread does, causing ports to remain open/locked. The ``raise_keyboard_interrupt`` option creates a unignorable signal which causes the whole application to shut-down (rudely). """ mon = Monitor(poll_interval=poll_interval) if extra_files is None: extra_files = [] mon.extra_files.extend(extra_files) t = threading.Thread(target=mon.periodic_reload) t.setDaemon(True) t.start() class classinstancemethod(object): """ Acts like a class method when called from a class, like an instance method when called by an instance. The method should take two arguments, 'self' and 'cls'; one of these will be None depending on how the method was called. """ def __init__(self, func): self.func = func self.__doc__ = func.__doc__ def __get__(self, obj, type=None): return _methodwrapper(self.func, obj=obj, type=type) class _methodwrapper(object): def __init__(self, func, obj, type): self.func = func self.obj = obj self.type = type def __call__(self, *args, **kw): assert not 'self' in kw and not 'cls' in kw, ( "You cannot use 'self' or 'cls' arguments to a " "classinstancemethod") return self.func(*((self.obj, self.type) + args), **kw) class Monitor(object): # pragma: no cover """ A file monitor and server restarter. Use this like: ..code-block:: Python install_reloader() Then make sure your server is installed with a shell script like:: err=3 while test "$err" -eq 3 ; do python server.py err="$?" done or is run from this .bat file (if you use Windows):: @echo off :repeat python server.py if %errorlevel% == 3 goto repeat or run a monitoring process in Python (``pserve --reload`` does this). Use the ``watch_file(filename)`` function to cause a reload/restart for other non-Python files (e.g., configuration files). If you have a dynamic set of files that grows over time you can use something like:: def watch_config_files(): return CONFIG_FILE_CACHE.keys() add_file_callback(watch_config_files) Then every time the reloader polls files it will call ``watch_config_files`` and check all the filenames it returns. """ instances = [] global_extra_files = [] global_file_callbacks = [] def __init__(self, poll_interval): self.module_mtimes = {} self.keep_running = True self.poll_interval = poll_interval self.extra_files = list(self.global_extra_files) self.instances.append(self) self.file_callbacks = list(self.global_file_callbacks) def _exit(self): # use os._exit() here and not sys.exit() since within a # thread sys.exit() just closes the given thread and # won't kill the process; note os._exit does not call # any atexit callbacks, nor does it do finally blocks, # flush open files, etc. In otherwords, it is rude. os._exit(3) def periodic_reload(self): while True: if not self.check_reload(): self._exit() break time.sleep(self.poll_interval) def check_reload(self): filenames = list(self.extra_files) for file_callback in self.file_callbacks: try: filenames.extend(file_callback()) except: print( "Error calling reloader callback %r:" % file_callback) traceback.print_exc() for module in sys.modules.values(): try: filename = module.__file__ except (AttributeError, ImportError): continue if filename is not None: filenames.append(filename) for filename in filenames: try: stat = os.stat(filename) if stat: mtime = stat.st_mtime else: mtime = 0 except (OSError, IOError): continue if filename.endswith('.pyc') and os.path.exists(filename[:-1]): mtime = max(os.stat(filename[:-1]).st_mtime, mtime) if not filename in self.module_mtimes: self.module_mtimes[filename] = mtime elif self.module_mtimes[filename] < mtime: print("%s changed; reloading..." % filename) return False return True def watch_file(self, cls, filename): """Watch the named file for changes""" filename = os.path.abspath(filename) if self is None: for instance in cls.instances: instance.watch_file(filename) cls.global_extra_files.append(filename) else: self.extra_files.append(filename) watch_file = classinstancemethod(watch_file) def add_file_callback(self, cls, callback): """Add a callback -- a function that takes no parameters -- that will return a list of filenames to watch for changes.""" if self is None: for instance in cls.instances: instance.add_file_callback(callback) cls.global_file_callbacks.append(callback) else: self.file_callbacks.append(callback) add_file_callback = classinstancemethod(add_file_callback) watch_file = Monitor.watch_file add_file_callback = Monitor.add_file_callback # For paste.deploy server instantiation (egg:pyramid#wsgiref) def wsgiref_server_runner(wsgi_app, global_conf, **kw): # pragma: no cover from wsgiref.simple_server import make_server host = kw.get('host', '0.0.0.0') port = int(kw.get('port', 8080)) server = make_server(host, port, wsgi_app) print('Starting HTTP server on http://%s:%s' % (host, port)) server.serve_forever() # For paste.deploy server instantiation (egg:pyramid#cherrypy) def cherrypy_server_runner( app, global_conf=None, host='127.0.0.1', port=None, ssl_pem=None, protocol_version=None, numthreads=None, server_name=None, max=None, request_queue_size=None, timeout=None ): # pragma: no cover """ Entry point for CherryPy's WSGI server Serves the specified WSGI app via CherryPyWSGIServer. ``app`` The WSGI 'application callable'; multiple WSGI applications may be passed as (script_name, callable) pairs. ``host`` This is the ipaddress to bind to (or a hostname if your nameserver is properly configured). This defaults to 127.0.0.1, which is not a public interface. ``port`` The port to run on, defaults to 8080 for HTTP, or 4443 for HTTPS. This can be a string or an integer value. ``ssl_pem`` This an optional SSL certificate file (via OpenSSL) You can generate a self-signed test PEM certificate file as follows: $ openssl genrsa 1024 > host.key $ chmod 400 host.key $ openssl req -new -x509 -nodes -sha1 -days 365 \\ -key host.key > host.cert $ cat host.cert host.key > host.pem $ chmod 400 host.pem ``protocol_version`` The protocol used by the server, by default ``HTTP/1.1``. ``numthreads`` The number of worker threads to create. ``server_name`` The string to set for WSGI's SERVER_NAME environ entry. ``max`` The maximum number of queued requests. (defaults to -1 = no limit). ``request_queue_size`` The 'backlog' argument to socket.listen(); specifies the maximum number of queued connections. ``timeout`` The timeout in seconds for accepted connections. """ is_ssl = False if ssl_pem: port = port or 4443 is_ssl = True if not port: if ':' in host: host, port = host.split(':', 1) else: port = 8080 bind_addr = (host, int(port)) kwargs = {} for var_name in ('numthreads', 'max', 'request_queue_size', 'timeout'): var = locals()[var_name] if var is not None: kwargs[var_name] = int(var) from cherrypy import wsgiserver server = wsgiserver.CherryPyWSGIServer(bind_addr, app, server_name=server_name, **kwargs) if ssl_pem is not None: import sys if sys.version_info < (3, 0): server.ssl_certificate = server.ssl_private_key = ssl_pem else: wsgiserver.get_ssl_adapter_class() # creates wsgiserver.ssl_builtin as side-effect server.ssl_adapter = wsgiserver.ssl_builtin.BuiltinSSLAdapter(ssl_pem, ssl_pem) if protocol_version: server.protocol = protocol_version try: protocol = is_ssl and 'https' or 'http' if host == '0.0.0.0': print('serving on 0.0.0.0:%s view at %s://127.0.0.1:%s' % (port, protocol, port)) else: print('serving on %s://%s:%s' % (protocol, host, port)) server.start() except (KeyboardInterrupt, SystemExit): server.stop() return server pyramid-1.4.5/pyramid/scripts/__init__.py0000664000175000017500000000001212203712502017712 0ustar takakitakaki# package pyramid-1.4.5/pyramid/decorator.py0000664000175000017500000000222012210154276016457 0ustar takakitakakiclass reify(object): """ Use as a class method decorator. It operates almost exactly like the Python ``@property`` decorator, but it puts the result of the method it decorates into the instance dict after the first call, effectively replacing the function it decorates with an instance variable. It is, in Python parlance, a non-data descriptor. An example: .. code-block:: python class Foo(object): @reify def jammy(self): print 'jammy called' return 1 And usage of Foo: .. code-block:: text >>> f = Foo() >>> v = f.jammy 'jammy called' >>> print v 1 >>> f.jammy 1 >>> # jammy func not called the second time; it replaced itself with 1 """ def __init__(self, wrapped): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: # pragma: no cover pass def __get__(self, inst, objtype=None): if inst is None: return self val = self.wrapped(inst) setattr(inst, self.wrapped.__name__, val) return val pyramid-1.4.5/pyramid/httpexceptions.py0000664000175000017500000010045612210154301017555 0ustar takakitakaki""" HTTP Exceptions --------------- This module contains Pyramid HTTP exception classes. Each class relates to a single HTTP status code. Each class is a subclass of the :class:`~HTTPException`. Each exception class is also a :term:`response` object. Each exception class has a status code according to `RFC 2068 `_: codes with 100-300 are not really errors; 400s are client errors, and 500s are server errors. Exception HTTPException HTTPOk * 200 - HTTPOk * 201 - HTTPCreated * 202 - HTTPAccepted * 203 - HTTPNonAuthoritativeInformation * 204 - HTTPNoContent * 205 - HTTPResetContent * 206 - HTTPPartialContent HTTPRedirection * 300 - HTTPMultipleChoices * 301 - HTTPMovedPermanently * 302 - HTTPFound * 303 - HTTPSeeOther * 304 - HTTPNotModified * 305 - HTTPUseProxy * 306 - Unused (not implemented, obviously) * 307 - HTTPTemporaryRedirect HTTPError HTTPClientError * 400 - HTTPBadRequest * 401 - HTTPUnauthorized * 402 - HTTPPaymentRequired * 403 - HTTPForbidden * 404 - HTTPNotFound * 405 - HTTPMethodNotAllowed * 406 - HTTPNotAcceptable * 407 - HTTPProxyAuthenticationRequired * 408 - HTTPRequestTimeout * 409 - HTTPConflict * 410 - HTTPGone * 411 - HTTPLengthRequired * 412 - HTTPPreconditionFailed * 413 - HTTPRequestEntityTooLarge * 414 - HTTPRequestURITooLong * 415 - HTTPUnsupportedMediaType * 416 - HTTPRequestRangeNotSatisfiable * 417 - HTTPExpectationFailed * 422 - HTTPUnprocessableEntity * 423 - HTTPLocked * 424 - HTTPFailedDependency HTTPServerError * 500 - HTTPInternalServerError * 501 - HTTPNotImplemented * 502 - HTTPBadGateway * 503 - HTTPServiceUnavailable * 504 - HTTPGatewayTimeout * 505 - HTTPVersionNotSupported * 507 - HTTPInsufficientStorage HTTP exceptions are also :term:`response` objects, thus they accept most of the same parameters that can be passed to a regular :class:`~pyramid.response.Response`. Each HTTP exception also has the following attributes: ``code`` the HTTP status code for the exception ``title`` remainder of the status line (stuff after the code) ``explanation`` a plain-text explanation of the error message that is not subject to environment or header substitutions; it is accessible in the template via ${explanation} ``detail`` a plain-text message customization that is not subject to environment or header substitutions; accessible in the template via ${detail} ``body_template`` a ``String.template``-format content fragment used for environment and header substitution; the default template includes both the explanation and further detail provided in the message. Each HTTP exception accepts the following parameters, any others will be forwarded to its :class:`~pyramid.response.Response` superclass: ``detail`` a plain-text override of the default ``detail`` ``headers`` a list of (k,v) header pairs ``comment`` a plain-text additional information which is usually stripped/hidden for end-users ``body_template`` a ``string.Template`` object containing a content fragment in HTML that frames the explanation and further detail ``body`` a string that will override the ``body_template`` and be used as the body of the response. Substitution of response headers into template values is always performed. Substitution of WSGI environment values is performed if a ``request`` is passed to the exception's constructor. The subclasses of :class:`~_HTTPMove` (:class:`~HTTPMultipleChoices`, :class:`~HTTPMovedPermanently`, :class:`~HTTPFound`, :class:`~HTTPSeeOther`, :class:`~HTTPUseProxy` and :class:`~HTTPTemporaryRedirect`) are redirections that require a ``Location`` field. Reflecting this, these subclasses have one additional keyword argument: ``location``, which indicates the location to which to redirect. """ from string import Template from zope.interface import implementer from webob import html_escape as _html_escape from pyramid.compat import ( class_types, text_type, binary_type, text_, ) from pyramid.interfaces import IExceptionResponse from pyramid.response import Response def _no_escape(value): if value is None: return '' if not isinstance(value, text_type): if hasattr(value, '__unicode__'): value = value.__unicode__() if isinstance(value, binary_type): value = text_(value, 'utf-8') else: value = text_type(value) return value @implementer(IExceptionResponse) class HTTPException(Response, Exception): ## You should set in subclasses: # code = 200 # title = 'OK' # explanation = 'why this happens' # body_template_obj = Template('response template') # differences from webob.exc.WSGIHTTPException: # # - doesn't use "strip_tags" (${br} placeholder for
, no other html # in default body template) # # - __call__ never generates a new Response, it always mutates self # # - explicitly sets self.message = detail to prevent whining by Python # 2.6.5+ access of Exception.message # # - its base class of HTTPException is no longer a Python 2.4 compatibility # shim; it's purely a base class that inherits from Exception. This # implies that this class' ``exception`` property always returns # ``self`` (it exists only for bw compat at this point). # # - documentation improvements (Pyramid-specific docstrings where necessary) # code = None title = None explanation = '' body_template_obj = Template('''\ ${explanation}${br}${br} ${detail} ${html_comment} ''') plain_template_obj = Template('''\ ${status} ${body}''') html_template_obj = Template('''\ ${status}

${status}

${body} ''') ## Set this to True for responses that should have no request body empty_body = False def __init__(self, detail=None, headers=None, comment=None, body_template=None, **kw): status = '%s %s' % (self.code, self.title) Response.__init__(self, status=status, **kw) Exception.__init__(self, detail) self.detail = self.message = detail if headers: self.headers.extend(headers) self.comment = comment if body_template is not None: self.body_template = body_template self.body_template_obj = Template(body_template) if self.empty_body: del self.content_type del self.content_length def __str__(self): return self.detail or self.explanation def prepare(self, environ): if not self.body and not self.empty_body: html_comment = '' comment = self.comment or '' accept = environ.get('HTTP_ACCEPT', '') if accept and 'html' in accept or '*/*' in accept: self.content_type = 'text/html' escape = _html_escape page_template = self.html_template_obj br = '
' if comment: html_comment = '' % escape(comment) else: self.content_type = 'text/plain' escape = _no_escape page_template = self.plain_template_obj br = '\n' if comment: html_comment = escape(comment) args = { 'br':br, 'explanation': escape(self.explanation), 'detail': escape(self.detail or ''), 'comment': escape(comment), 'html_comment':html_comment, } body_tmpl = self.body_template_obj if HTTPException.body_template_obj is not body_tmpl: # Custom template; add headers to args for k, v in environ.items(): if (not k.startswith('wsgi.')) and ('.' in k): # omit custom environ variables, stringifying them may # trigger code that should not be executed here; see # https://github.com/Pylons/pyramid/issues/239 continue args[k] = escape(v) for k, v in self.headers.items(): args[k.lower()] = escape(v) body = body_tmpl.substitute(args) page = page_template.substitute(status=self.status, body=body) if isinstance(page, text_type): page = page.encode(self.charset) self.app_iter = [page] self.body = page @property def wsgi_response(self): # bw compat only return self exception = wsgi_response # bw compat only def __call__(self, environ, start_response): # differences from webob.exc.WSGIHTTPException # # - does not try to deal with HEAD requests # # - does not manufacture a new response object when generating # the default response # self.prepare(environ) return Response.__call__(self, environ, start_response) WSGIHTTPException = HTTPException # b/c post 1.5 class HTTPError(HTTPException): """ base class for exceptions with status codes in the 400s and 500s This is an exception which indicates that an error has occurred, and that any work in progress should not be committed. """ class HTTPRedirection(HTTPException): """ base class for exceptions with status codes in the 300s (redirections) This is an abstract base class for 3xx redirection. It indicates that further action needs to be taken by the user agent in order to fulfill the request. It does not necessarly signal an error condition. """ class HTTPOk(HTTPException): """ Base class for exceptions with status codes in the 200s (successful responses) code: 200, title: OK """ code = 200 title = 'OK' ############################################################ ## 2xx success ############################################################ class HTTPCreated(HTTPOk): """ subclass of :class:`~HTTPOk` This indicates that request has been fulfilled and resulted in a new resource being created. code: 201, title: Created """ code = 201 title = 'Created' class HTTPAccepted(HTTPOk): """ subclass of :class:`~HTTPOk` This indicates that the request has been accepted for processing, but the processing has not been completed. code: 202, title: Accepted """ code = 202 title = 'Accepted' explanation = 'The request is accepted for processing.' class HTTPNonAuthoritativeInformation(HTTPOk): """ subclass of :class:`~HTTPOk` This indicates that the returned metainformation in the entity-header is not the definitive set as available from the origin server, but is gathered from a local or a third-party copy. code: 203, title: Non-Authoritative Information """ code = 203 title = 'Non-Authoritative Information' class HTTPNoContent(HTTPOk): """ subclass of :class:`~HTTPOk` This indicates that the server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. code: 204, title: No Content """ code = 204 title = 'No Content' empty_body = True class HTTPResetContent(HTTPOk): """ subclass of :class:`~HTTPOk` This indicates that the server has fulfilled the request and the user agent SHOULD reset the document view which caused the request to be sent. code: 205, title: Reset Content """ code = 205 title = 'Reset Content' empty_body = True class HTTPPartialContent(HTTPOk): """ subclass of :class:`~HTTPOk` This indicates that the server has fulfilled the partial GET request for the resource. code: 206, title: Partial Content """ code = 206 title = 'Partial Content' ## FIXME: add 207 Multi-Status (but it's complicated) ############################################################ ## 3xx redirection ############################################################ class _HTTPMove(HTTPRedirection): """ redirections which require a Location field Since a 'Location' header is a required attribute of 301, 302, 303, 305 and 307 (but not 304), this base class provides the mechanics to make this easy. You must provide a ``location`` keyword argument. """ # differences from webob.exc._HTTPMove: # # - ${location} isn't wrapped in an tag in body # # - location keyword arg defaults to '' # # - location isn't prepended with req.path_url when adding it as # a header # # - ``location`` is first keyword (and positional) argument # # - ``add_slash`` argument is no longer accepted: code that passes # add_slash argument to the constructor will receive an exception. explanation = 'The resource has been moved to' body_template_obj = Template('''\ ${explanation} ${location}; you should be redirected automatically. ${detail} ${html_comment}''') def __init__(self, location='', detail=None, headers=None, comment=None, body_template=None, **kw): if location is None: raise ValueError("HTTP redirects need a location to redirect to.") super(_HTTPMove, self).__init__( detail=detail, headers=headers, comment=comment, body_template=body_template, location=location, **kw) class HTTPMultipleChoices(_HTTPMove): """ subclass of :class:`~_HTTPMove` This indicates that the requested resource corresponds to any one of a set of representations, each with its own specific location, and agent-driven negotiation information is being provided so that the user can select a preferred representation and redirect its request to that location. code: 300, title: Multiple Choices """ code = 300 title = 'Multiple Choices' class HTTPMovedPermanently(_HTTPMove): """ subclass of :class:`~_HTTPMove` This indicates that the requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs. code: 301, title: Moved Permanently """ code = 301 title = 'Moved Permanently' class HTTPFound(_HTTPMove): """ subclass of :class:`~_HTTPMove` This indicates that the requested resource resides temporarily under a different URI. code: 302, title: Found """ code = 302 title = 'Found' explanation = 'The resource was found at' # This one is safe after a POST (the redirected location will be # retrieved with GET): class HTTPSeeOther(_HTTPMove): """ subclass of :class:`~_HTTPMove` This indicates that the response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource. code: 303, title: See Other """ code = 303 title = 'See Other' class HTTPNotModified(HTTPRedirection): """ subclass of :class:`~HTTPRedirection` This indicates that if the client has performed a conditional GET request and access is allowed, but the document has not been modified, the server SHOULD respond with this status code. code: 304, title: Not Modified """ # FIXME: this should include a date or etag header code = 304 title = 'Not Modified' empty_body = True class HTTPUseProxy(_HTTPMove): """ subclass of :class:`~_HTTPMove` This indicates that the requested resource MUST be accessed through the proxy given by the Location field. code: 305, title: Use Proxy """ # Not a move, but looks a little like one code = 305 title = 'Use Proxy' explanation = ( 'The resource must be accessed through a proxy located at') class HTTPTemporaryRedirect(_HTTPMove): """ subclass of :class:`~_HTTPMove` This indicates that the requested resource resides temporarily under a different URI. code: 307, title: Temporary Redirect """ code = 307 title = 'Temporary Redirect' ############################################################ ## 4xx client error ############################################################ class HTTPClientError(HTTPError): """ base class for the 400s, where the client is in error This is an error condition in which the client is presumed to be in-error. This is an expected problem, and thus is not considered a bug. A server-side traceback is not warranted. Unless specialized, this is a '400 Bad Request' """ code = 400 title = 'Bad Request' explanation = ('The server could not comply with the request since ' 'it is either malformed or otherwise incorrect.') class HTTPBadRequest(HTTPClientError): pass class HTTPUnauthorized(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the request requires user authentication. code: 401, title: Unauthorized """ code = 401 title = 'Unauthorized' explanation = ( 'This server could not verify that you are authorized to ' 'access the document you requested. Either you supplied the ' 'wrong credentials (e.g., bad password), or your browser ' 'does not understand how to supply the credentials required.') class HTTPPaymentRequired(HTTPClientError): """ subclass of :class:`~HTTPClientError` code: 402, title: Payment Required """ code = 402 title = 'Payment Required' explanation = ('Access was denied for financial reasons.') class HTTPForbidden(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server understood the request, but is refusing to fulfill it. code: 403, title: Forbidden Raise this exception within :term:`view` code to immediately return the :term:`forbidden view` to the invoking user. Usually this is a basic ``403`` page, but the forbidden view can be customized as necessary. See :ref:`changing_the_forbidden_view`. A ``Forbidden`` exception will be the ``context`` of a :term:`Forbidden View`. This exception's constructor treats two arguments specially. The first argument, ``detail``, should be a string. The value of this string will be used as the ``message`` attribute of the exception object. The second special keyword argument, ``result`` is usually an instance of :class:`pyramid.security.Denied` or :class:`pyramid.security.ACLDenied` each of which indicates a reason for the forbidden error. However, ``result`` is also permitted to be just a plain boolean ``False`` object or ``None``. The ``result`` value will be used as the ``result`` attribute of the exception object. It defaults to ``None``. The :term:`Forbidden View` can use the attributes of a Forbidden exception as necessary to provide extended information in an error report shown to a user. """ # differences from webob.exc.HTTPForbidden: # # - accepts a ``result`` keyword argument # # - overrides constructor to set ``self.result`` # # differences from older ``pyramid.exceptions.Forbidden``: # # - ``result`` must be passed as a keyword argument. # code = 403 title = 'Forbidden' explanation = ('Access was denied to this resource.') def __init__(self, detail=None, headers=None, comment=None, body_template=None, result=None, **kw): HTTPClientError.__init__(self, detail=detail, headers=headers, comment=comment, body_template=body_template, **kw) self.result = result class HTTPNotFound(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server did not find anything matching the Request-URI. code: 404, title: Not Found Raise this exception within :term:`view` code to immediately return the :term:`Not Found view` to the invoking user. Usually this is a basic ``404`` page, but the Not Found view can be customized as necessary. See :ref:`changing_the_notfound_view`. This exception's constructor accepts a ``detail`` argument (the first argument), which should be a string. The value of this string will be available as the ``message`` attribute of this exception, for availability to the :term:`Not Found View`. """ code = 404 title = 'Not Found' explanation = ('The resource could not be found.') class HTTPMethodNotAllowed(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the method specified in the Request-Line is not allowed for the resource identified by the Request-URI. code: 405, title: Method Not Allowed """ # differences from webob.exc.HTTPMethodNotAllowed: # # - body_template_obj uses ${br} instead of
code = 405 title = 'Method Not Allowed' body_template_obj = Template('''\ The method ${REQUEST_METHOD} is not allowed for this resource. ${br}${br} ${detail}''') class HTTPNotAcceptable(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates the resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. code: 406, title: Not Acceptable """ # differences from webob.exc.HTTPNotAcceptable: # # - "template" attribute left off (useless, bug in webob?) code = 406 title = 'Not Acceptable' class HTTPProxyAuthenticationRequired(HTTPClientError): """ subclass of :class:`~HTTPClientError` This is similar to 401, but indicates that the client must first authenticate itself with the proxy. code: 407, title: Proxy Authentication Required """ code = 407 title = 'Proxy Authentication Required' explanation = ('Authentication with a local proxy is needed.') class HTTPRequestTimeout(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the client did not produce a request within the time that the server was prepared to wait. code: 408, title: Request Timeout """ code = 408 title = 'Request Timeout' explanation = ('The server has waited too long for the request to ' 'be sent by the client.') class HTTPConflict(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the request could not be completed due to a conflict with the current state of the resource. code: 409, title: Conflict """ code = 409 title = 'Conflict' explanation = ('There was a conflict when trying to complete ' 'your request.') class HTTPGone(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the requested resource is no longer available at the server and no forwarding address is known. code: 410, title: Gone """ code = 410 title = 'Gone' explanation = ('This resource is no longer available. No forwarding ' 'address is given.') class HTTPLengthRequired(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server refuses to accept the request without a defined Content-Length. code: 411, title: Length Required """ code = 411 title = 'Length Required' explanation = ('Content-Length header required.') class HTTPPreconditionFailed(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the precondition given in one or more of the request-header fields evaluated to false when it was tested on the server. code: 412, title: Precondition Failed """ code = 412 title = 'Precondition Failed' explanation = ('Request precondition failed.') class HTTPRequestEntityTooLarge(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server is refusing to process a request because the request entity is larger than the server is willing or able to process. code: 413, title: Request Entity Too Large """ code = 413 title = 'Request Entity Too Large' explanation = ('The body of your request was too large for this server.') class HTTPRequestURITooLong(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. code: 414, title: Request-URI Too Long """ code = 414 title = 'Request-URI Too Long' explanation = ('The request URI was too long for this server.') class HTTPUnsupportedMediaType(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method. code: 415, title: Unsupported Media Type """ # differences from webob.exc.HTTPUnsupportedMediaType: # # - "template_obj" attribute left off (useless, bug in webob?) code = 415 title = 'Unsupported Media Type' class HTTPRequestRangeNotSatisfiable(HTTPClientError): """ subclass of :class:`~HTTPClientError` The server SHOULD return a response with this status code if a request included a Range request-header field, and none of the range-specifier values in this field overlap the current extent of the selected resource, and the request did not include an If-Range request-header field. code: 416, title: Request Range Not Satisfiable """ code = 416 title = 'Request Range Not Satisfiable' explanation = ('The Range requested is not available.') class HTTPExpectationFailed(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indidcates that the expectation given in an Expect request-header field could not be met by this server. code: 417, title: Expectation Failed """ code = 417 title = 'Expectation Failed' explanation = ('Expectation failed.') class HTTPUnprocessableEntity(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the server is unable to process the contained instructions. Only for WebDAV. code: 422, title: Unprocessable Entity """ ## Note: from WebDAV code = 422 title = 'Unprocessable Entity' explanation = 'Unable to process the contained instructions' class HTTPLocked(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the resource is locked. Only for WebDAV code: 423, title: Locked """ ## Note: from WebDAV code = 423 title = 'Locked' explanation = ('The resource is locked') class HTTPFailedDependency(HTTPClientError): """ subclass of :class:`~HTTPClientError` This indicates that the method could not be performed because the requested action depended on another action and that action failed. Only for WebDAV. code: 424, title: Failed Dependency """ ## Note: from WebDAV code = 424 title = 'Failed Dependency' explanation = ( 'The method could not be performed because the requested ' 'action dependended on another action and that action failed') ############################################################ ## 5xx Server Error ############################################################ # Response status codes beginning with the digit "5" indicate cases in # which the server is aware that it has erred or is incapable of # performing the request. Except when responding to a HEAD request, the # server SHOULD include an entity containing an explanation of the error # situation, and whether it is a temporary or permanent condition. User # agents SHOULD display any included entity to the user. These response # codes are applicable to any request method. class HTTPServerError(HTTPError): """ base class for the 500s, where the server is in-error This is an error condition in which the server is presumed to be in-error. Unless specialized, this is a '500 Internal Server Error'. """ code = 500 title = 'Internal Server Error' explanation = ( 'The server has either erred or is incapable of performing ' 'the requested operation.') class HTTPInternalServerError(HTTPServerError): pass class HTTPNotImplemented(HTTPServerError): """ subclass of :class:`~HTTPServerError` This indicates that the server does not support the functionality required to fulfill the request. code: 501, title: Not Implemented """ # differences from webob.exc.HTTPNotAcceptable: # # - "template" attr left off (useless, bug in webob?) code = 501 title = 'Not Implemented' class HTTPBadGateway(HTTPServerError): """ subclass of :class:`~HTTPServerError` This indicates that the server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request. code: 502, title: Bad Gateway """ code = 502 title = 'Bad Gateway' explanation = ('Bad gateway.') class HTTPServiceUnavailable(HTTPServerError): """ subclass of :class:`~HTTPServerError` This indicates that the server is currently unable to handle the request due to a temporary overloading or maintenance of the server. code: 503, title: Service Unavailable """ code = 503 title = 'Service Unavailable' explanation = ('The server is currently unavailable. ' 'Please try again at a later time.') class HTTPGatewayTimeout(HTTPServerError): """ subclass of :class:`~HTTPServerError` This indicates that the server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in attempting to complete the request. code: 504, title: Gateway Timeout """ code = 504 title = 'Gateway Timeout' explanation = ('The gateway has timed out.') class HTTPVersionNotSupported(HTTPServerError): """ subclass of :class:`~HTTPServerError` This indicates that the server does not support, or refuses to support, the HTTP protocol version that was used in the request message. code: 505, title: HTTP Version Not Supported """ code = 505 title = 'HTTP Version Not Supported' explanation = ('The HTTP version is not supported.') class HTTPInsufficientStorage(HTTPServerError): """ subclass of :class:`~HTTPServerError` This indicates that the server does not have enough space to save the resource. code: 507, title: Insufficient Storage """ code = 507 title = 'Insufficient Storage' explanation = ('There was not enough space to save the resource') def exception_response(status_code, **kw): """Creates an HTTP exception based on a status code. Example:: raise exception_response(404) # raises an HTTPNotFound exception. The values passed as ``kw`` are provided to the exception's constructor. """ exc = status_map[status_code](**kw) return exc def default_exceptionresponse_view(context, request): if not isinstance(context, Exception): # backwards compat for an exception response view registered via # config.set_notfound_view or config.set_forbidden_view # instead of as a proper exception view context = request.exception or context return context # assumed to be an IResponse status_map={} code = None for name, value in list(globals().items()): if (isinstance(value, class_types) and issubclass(value, HTTPException) and not name.startswith('_')): code = getattr(value, 'code', None) if code: status_map[code] = value del name, value, code pyramid-1.4.5/pyramid/events.py0000664000175000017500000002145212210154276016011 0ustar takakitakakiimport venusian from zope.interface import ( implementer, Interface ) from pyramid.interfaces import ( IContextFound, INewRequest, INewResponse, IApplicationCreated, IBeforeRender, ) class subscriber(object): """ Decorator activated via a :term:`scan` which treats the function being decorated as an event subscriber for the set of interfaces passed as ``*ifaces`` and the set of predicate terms passed as ``**predicates`` to the decorator constructor. For example: .. code-block:: python from pyramid.events import NewRequest from pyramid.events import subscriber @subscriber(NewRequest) def mysubscriber(event): event.request.foo = 1 More than one event type can be passed as a constructor argument. The decorated subscriber will be called for each event type. .. code-block:: python from pyramid.events import NewRequest, NewResponse from pyramid.events import subscriber @subscriber(NewRequest, NewResponse) def mysubscriber(event): print event When the ``subscriber`` decorator is used without passing an arguments, the function it decorates is called for every event sent: .. code-block:: python from pyramid.events import subscriber @subscriber() def mysubscriber(event): print event This method will have no effect until a :term:`scan` is performed against the package or module which contains it, ala: .. code-block:: python from pyramid.config import Configurator config = Configurator() config.scan('somepackage_containing_subscribers') Any ``**predicate`` arguments will be passed along to :meth:`pyramid.config.Configurator.add_subscriber`. See :ref:`subscriber_predicates` for a description of how predicates can narrow the set of circumstances in which a subscriber will be called. """ venusian = venusian # for unit testing def __init__(self, *ifaces, **predicates): self.ifaces = ifaces self.predicates = predicates def register(self, scanner, name, wrapped): config = scanner.config for iface in self.ifaces or (Interface,): config.add_subscriber(wrapped, iface, **self.predicates) def __call__(self, wrapped): self.venusian.attach(wrapped, self.register, category='pyramid') return wrapped @implementer(INewRequest) class NewRequest(object): """ An instance of this class is emitted as an :term:`event` whenever :app:`Pyramid` begins to process a new request. The event instance has an attribute, ``request``, which is a :term:`request` object. This event class implements the :class:`pyramid.interfaces.INewRequest` interface.""" def __init__(self, request): self.request = request @implementer(INewResponse) class NewResponse(object): """ An instance of this class is emitted as an :term:`event` whenever any :app:`Pyramid` :term:`view` or :term:`exception view` returns a :term:`response`. The instance has two attributes:``request``, which is the request which caused the response, and ``response``, which is the response object returned by a view or renderer. If the ``response`` was generated by an :term:`exception view`, the request will have an attribute named ``exception``, which is the exception object which caused the exception view to be executed. If the response was generated by a 'normal' view, this attribute of the request will be ``None``. This event will not be generated if a response cannot be created due to an exception that is not caught by an exception view (no response is created under this circumstace). This class implements the :class:`pyramid.interfaces.INewResponse` interface. .. note:: Postprocessing a response is usually better handled in a WSGI :term:`middleware` component than in subscriber code that is called by a :class:`pyramid.interfaces.INewResponse` event. The :class:`pyramid.interfaces.INewResponse` event exists almost purely for symmetry with the :class:`pyramid.interfaces.INewRequest` event. """ def __init__(self, request, response): self.request = request self.response = response @implementer(IContextFound) class ContextFound(object): """ An instance of this class is emitted as an :term:`event` after the :app:`Pyramid` :term:`router` finds a :term:`context` object (after it performs traversal) but before any view code is executed. The instance has an attribute, ``request``, which is the request object generated by :app:`Pyramid`. Notably, the request object will have an attribute named ``context``, which is the context that will be provided to the view which will eventually be called, as well as other attributes attached by context-finding code. This class implements the :class:`pyramid.interfaces.IContextFound` interface. .. note:: As of :app:`Pyramid` 1.0, for backwards compatibility purposes, this event may also be imported as :class:`pyramid.events.AfterTraversal`. """ def __init__(self, request): self.request = request AfterTraversal = ContextFound # b/c as of 1.0 @implementer(IApplicationCreated) class ApplicationCreated(object): """ An instance of this class is emitted as an :term:`event` when the :meth:`pyramid.config.Configurator.make_wsgi_app` is called. The instance has an attribute, ``app``, which is an instance of the :term:`router` that will handle WSGI requests. This class implements the :class:`pyramid.interfaces.IApplicationCreated` interface. .. note:: For backwards compatibility purposes, this class can also be imported as :class:`pyramid.events.WSGIApplicationCreatedEvent`. This was the name of the event class before :app:`Pyramid` 1.0. """ def __init__(self, app): self.app = app self.object = app WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.0) @implementer(IBeforeRender) class BeforeRender(dict): """ Subscribers to this event may introspect and modify the set of :term:`renderer globals` before they are passed to a :term:`renderer`. This event object iself has a dictionary-like interface that can be used for this purpose. For example:: from pyramid.events import subscriber from pyramid.events import BeforeRender @subscriber(BeforeRender) def add_global(event): event['mykey'] = 'foo' An object of this type is sent as an event just before a :term:`renderer` is invoked (but *after* the -- deprecated -- application-level renderer globals factory added via :class:`pyramid.config.Configurator.set_renderer_globals_factory`, if any, has injected its own keys into the renderer globals dictionary). If a subscriber adds a key via ``__setitem__`` that already exists in the renderer globals dictionary, it will overwrite the older value there. This can be problematic because event subscribers to the BeforeRender event do not possess any relative ordering. For maximum interoperability with other third-party subscribers, if you write an event subscriber meant to be used as a BeforeRender subscriber, your subscriber code will need to ensure no value already exists in the renderer globals dictionary before setting an overriding value (which can be done using ``.get`` or ``__contains__`` of the event object). The dictionary returned from the view is accessible through the :attr:`rendering_val` attribute of a :class:`~pyramid.events.BeforeRender` event. Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from your view callable, like so:: from pyramid.view import view_config @view_config(renderer='some_renderer') def myview(request): return {'mykey': 'somevalue', 'mykey2': 'somevalue2'} :attr:`rendering_val` can be used to access these values from the :class:`~pyramid.events.BeforeRender` object:: from pyramid.events import subscriber from pyramid.events import BeforeRender @subscriber(BeforeRender) def read_return(event): # {'mykey': 'somevalue'} is returned from the view print(event.rendering_val['mykey']) In other words, :attr:`rendering_val` is the (non-system) value returned by a view or passed to ``render*`` as ``value``. This feature is new in Pyramid 1.2. For a description of the values present in the renderer globals dictionary, see :ref:`renderer_system_values`. See also :class:`pyramid.interfaces.IBeforeRender`. """ def __init__(self, system, rendering_val=None): dict.__init__(self, system) self.rendering_val = rendering_val pyramid-1.4.5/pyramid/view.py0000664000175000017500000004400312210154276015454 0ustar takakitakakiimport venusian from zope.interface import providedBy from zope.deprecation import deprecated from pyramid.interfaces import ( IRoutesMapper, IView, IViewClassifier, ) from pyramid.compat import ( map_, decode_path_info, ) from pyramid.httpexceptions import ( HTTPFound, default_exceptionresponse_view, ) from pyramid.path import caller_package from pyramid.static import static_view from pyramid.threadlocal import get_current_registry _marker = object() class static(static_view): """ Backwards compatibility alias for :class:`pyramid.static.static_view`; it overrides that class' constructor to pass ``use_subpath=True`` by default. This class is deprecated as of :app:`Pyramid` 1.1. Use :class:`pyramid.static.static_view` instead (probably with a ``use_subpath=True`` argument). """ def __init__(self, root_dir, cache_max_age=3600, package_name=None): if package_name is None: package_name = caller_package().__name__ static_view.__init__(self, root_dir, cache_max_age=cache_max_age, package_name=package_name, use_subpath=True) deprecated( 'static', 'The "pyramid.view.static" class is deprecated as of Pyramid 1.1; ' 'use the "pyramid.static.static_view" class instead with the ' '"use_subpath" argument set to True.') def render_view_to_response(context, request, name='', secure=True): """ Call the :term:`view callable` configured with a :term:`view configuration` that matches the :term:`view name` ``name`` registered against the specified ``context`` and ``request`` and return a :term:`response` object. This function will return ``None`` if a corresponding :term:`view callable` cannot be found (when no :term:`view configuration` matches the combination of ``name`` / ``context`` / and ``request``). If `secure`` is ``True``, and the :term:`view callable` found is protected by a permission, the permission will be checked before calling the view function. If the permission check disallows view execution (based on the current :term:`authorization policy`), a :exc:`pyramid.httpexceptions.HTTPForbidden` exception will be raised. The exception's ``args`` attribute explains why the view access was disallowed. If ``secure`` is ``False``, no permission checking is done.""" provides = [IViewClassifier] + map_(providedBy, (request, context)) try: reg = request.registry except AttributeError: reg = get_current_registry() view = reg.adapters.lookup(provides, IView, name=name) if view is None: return None if not secure: # the view will have a __call_permissive__ attribute if it's # secured; otherwise it won't. view = getattr(view, '__call_permissive__', view) # if this view is secured, it will raise a Forbidden # appropriately if the executing user does not have the proper # permission return view(context, request) def render_view_to_iterable(context, request, name='', secure=True): """ Call the :term:`view callable` configured with a :term:`view configuration` that matches the :term:`view name` ``name`` registered against the specified ``context`` and ``request`` and return an iterable object which represents the body of a response. This function will return ``None`` if a corresponding :term:`view callable` cannot be found (when no :term:`view configuration` matches the combination of ``name`` / ``context`` / and ``request``). Additionally, this function will raise a :exc:`ValueError` if a view function is found and called but the view function's result does not have an ``app_iter`` attribute. You can usually get the bytestring representation of the return value of this function by calling ``b''.join(iterable)``, or just use :func:`pyramid.view.render_view` instead. If ``secure`` is ``True``, and the view is protected by a permission, the permission will be checked before the view function is invoked. If the permission check disallows view execution (based on the current :term:`authentication policy`), a :exc:`pyramid.httpexceptions.HTTPForbidden` exception will be raised; its ``args`` attribute explains why the view access was disallowed. If ``secure`` is ``False``, no permission checking is done.""" response = render_view_to_response(context, request, name, secure) if response is None: return None return response.app_iter def render_view(context, request, name='', secure=True): """ Call the :term:`view callable` configured with a :term:`view configuration` that matches the :term:`view name` ``name`` registered against the specified ``context`` and ``request`` and unwind the view response's ``app_iter`` (see :ref:`the_response`) into a single bytestring. This function will return ``None`` if a corresponding :term:`view callable` cannot be found (when no :term:`view configuration` matches the combination of ``name`` / ``context`` / and ``request``). Additionally, this function will raise a :exc:`ValueError` if a view function is found and called but the view function's result does not have an ``app_iter`` attribute. This function will return ``None`` if a corresponding view cannot be found. If ``secure`` is ``True``, and the view is protected by a permission, the permission will be checked before the view is invoked. If the permission check disallows view execution (based on the current :term:`authorization policy`), a :exc:`pyramid.httpexceptions.HTTPForbidden` exception will be raised; its ``args`` attribute explains why the view access was disallowed. If ``secure`` is ``False``, no permission checking is done.""" iterable = render_view_to_iterable(context, request, name, secure) if iterable is None: return None return b''.join(iterable) class view_config(object): """ A function, class or method :term:`decorator` which allows a developer to create view registrations nearer to a :term:`view callable` definition than use :term:`imperative configuration` to do the same. For example, this code in a module ``views.py``:: from resources import MyResource @view_config(name='my_view', context=MyResource, permission='read', route_name='site1') def my_view(context, request): return 'OK' Might replace the following call to the :meth:`pyramid.config.Configurator.add_view` method:: import views from resources import MyResource config.add_view(views.my_view, context=MyResource, name='my_view', permission='read', 'route_name='site1') .. note: :class:`pyramid.view.view_config` is also importable, for backwards compatibility purposes, as the name :class:`pyramid.view.bfg_view`. The following keyword arguments are supported to :class:`pyramid.view.view_config`: ``context``, ``permission``, ``name``, ``request_type``, ``route_name``, ``request_method``, ``request_param``, ``containment``, ``xhr``, ``accept``, ``header``, ``path_info``, ``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``, ``match_param``, ``csrf_token``, ``physical_path``, and ``predicates``. The meanings of these arguments are the same as the arguments passed to :meth:`pyramid.config.Configurator.add_view`. If any argument is left out, its default will be the equivalent ``add_view`` default. An additional keyword argument named ``_depth`` is provided for people who wish to reuse this class from another decorator. The default value is ``0`` and should be specified relative to the ``view_config`` invocation. It will be passed in to the :term:`venusian` ``attach`` function as the depth of the callstack when Venusian checks if the decorator is being used in a class or module context. It's not often used, but it can be useful in this circumstance. See the ``attach`` function in Venusian for more information. See :ref:`mapping_views_using_a_decorator_section` for details about using :class:`pyramid.view.view_config`. """ venusian = venusian # for testing injection def __init__(self, **settings): if 'for_' in settings: if settings.get('context') is None: settings['context'] = settings['for_'] self.__dict__.update(settings) def __call__(self, wrapped): settings = self.__dict__.copy() depth = settings.pop('_depth', 0) def callback(context, name, ob): config = context.config.with_package(info.module) config.add_view(view=ob, **settings) info = self.venusian.attach(wrapped, callback, category='pyramid', depth=depth + 1) if info.scope == 'class': # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if settings.get('attr') is None: settings['attr'] = wrapped.__name__ settings['_info'] = info.codeinfo # fbo "action_method" return wrapped bfg_view = view_config # bw compat (forever) class view_defaults(view_config): """ A class :term:`decorator` which, when applied to a class, will provide defaults for all view configurations that use the class. This decorator accepts all the arguments accepted by :class:`pyramid.config.view_config`, and each has the same meaning. See :ref:`view_defaults` for more information. """ def __call__(self, wrapped): wrapped.__view_defaults__ = self.__dict__.copy() return wrapped class AppendSlashNotFoundViewFactory(object): """ There can only be one :term:`Not Found view` in any :app:`Pyramid` application. Even if you use :func:`pyramid.view.append_slash_notfound_view` as the Not Found view, :app:`Pyramid` still must generate a ``404 Not Found`` response when it cannot redirect to a slash-appended URL; this not found response will be visible to site users. If you don't care what this 404 response looks like, and you only need redirections to slash-appended route URLs, you may use the :func:`pyramid.view.append_slash_notfound_view` object as the Not Found view. However, if you wish to use a *custom* notfound view callable when a URL cannot be redirected to a slash-appended URL, you may wish to use an instance of this class as the Not Found view, supplying a :term:`view callable` to be used as the custom notfound view as the first argument to its constructor. For instance: .. code-block:: python from pyramid.httpexceptions import HTTPNotFound from pyramid.view import AppendSlashNotFoundViewFactory def notfound_view(context, request): return HTTPNotFound('nope') custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) config.add_view(custom_append_slash, context=HTTPNotFound) The ``notfound_view`` supplied must adhere to the two-argument view callable calling convention of ``(context, request)`` (``context`` will be the exception object). """ def __init__(self, notfound_view=None): if notfound_view is None: notfound_view = default_exceptionresponse_view self.notfound_view = notfound_view def __call__(self, context, request): path = decode_path_info(request.environ['PATH_INFO'] or '/') registry = request.registry mapper = registry.queryUtility(IRoutesMapper) if mapper is not None and not path.endswith('/'): slashpath = path + '/' for route in mapper.get_routes(): if route.match(slashpath) is not None: qs = request.query_string if qs: qs = '?' + qs return HTTPFound(location=request.path+'/'+qs) return self.notfound_view(context, request) append_slash_notfound_view = AppendSlashNotFoundViewFactory() append_slash_notfound_view.__doc__ = """\ For behavior like Django's ``APPEND_SLASH=True``, use this view as the :term:`Not Found view` in your application. When this view is the Not Found view (indicating that no view was found), and any routes have been defined in the configuration of your application, if the value of the ``PATH_INFO`` WSGI environment variable does not already end in a slash, and if the value of ``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP redirect to the slash-appended PATH_INFO. Note that this will *lose* ``POST`` data information (turning it into a GET), so you shouldn't rely on this to redirect POST requests. Note also that static routes are not considered when attempting to find a matching route. Use the :meth:`pyramid.config.Configurator.add_view` method to configure this view as the Not Found view:: from pyramid.httpexceptions import HTTPNotFound from pyramid.view import append_slash_notfound_view config.add_view(append_slash_notfound_view, context=HTTPNotFound) See also :ref:`changing_the_notfound_view`. """ class notfound_view_config(object): """ An analogue of :class:`pyramid.view.view_config` which registers a :term:`not found view`. The notfound_view_config constructor accepts most of the same arguments as the constructor of :class:`pyramid.view.view_config`. It can be used in the same places, and behaves in largely the same way, except it always registers a not found exception view instead of a 'normal' view. Example: .. code-block:: python from pyramid.view import notfound_view_config from pyramid.response import Response @notfound_view_config() def notfound(request): return Response('Not found, dude!', status='404 Not Found') All arguments except ``append_slash`` have the same meaning as :meth:`pyramid.view.view_config` and each predicate argument restricts the set of circumstances under which this notfound view will be invoked. If ``append_slash`` is ``True``, when the 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. See :ref:`changing_the_notfound_view` for detailed usage information. .. note:: This class is new as of Pyramid 1.3. """ venusian = venusian def __init__(self, **settings): self.__dict__.update(settings) def __call__(self, wrapped): settings = self.__dict__.copy() def callback(context, name, ob): config = context.config.with_package(info.module) config.add_notfound_view(view=ob, **settings) info = self.venusian.attach(wrapped, callback, category='pyramid') if info.scope == 'class': # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if settings.get('attr') is None: settings['attr'] = wrapped.__name__ settings['_info'] = info.codeinfo # fbo "action_method" return wrapped class forbidden_view_config(object): """ An analogue of :class:`pyramid.view.view_config` which registers a :term:`forbidden view`. The forbidden_view_config constructor accepts most of the same arguments as the constructor of :class:`pyramid.view.view_config`. It can be used in the same places, and behaves in largely the same way, except it always registers a forbidden exception view instead of a 'normal' view. Example: .. code-block:: python from pyramid.view import forbidden_view_config from pyramid.response import Response @forbidden_view_config() def notfound(request): return Response('You are not allowed', status='401 Unauthorized') All arguments passed to this function have the same meaning as :meth:`pyramid.view.view_config` and each predicate argument restricts the set of circumstances under which this notfound view will be invoked. See :ref:`changing_the_forbidden_view` for detailed usage information. .. note:: This class is new as of Pyramid 1.3. """ venusian = venusian def __init__(self, **settings): self.__dict__.update(settings) def __call__(self, wrapped): settings = self.__dict__.copy() def callback(context, name, ob): config = context.config.with_package(info.module) config.add_forbidden_view(view=ob, **settings) info = self.venusian.attach(wrapped, callback, category='pyramid') if info.scope == 'class': # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there if settings.get('attr') is None: settings['attr'] = wrapped.__name__ settings['_info'] = info.codeinfo # fbo "action_method" return wrapped def is_response(ob): """ Return ``True`` if ``ob`` implements the interface implied by :ref:`the_response`. ``False`` if not. .. warning:: This function is deprecated as of :app:`Pyramid` 1.1. New code should not use it. Instead, new code should use the :func:`pyramid.request.Request.is_response` method.""" if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and hasattr(ob, 'status') ): return True return False deprecated( 'is_response', 'pyramid.view.is_response is deprecated as of Pyramid 1.1. Use ' 'pyramid.request.Request.is_response instead.') pyramid-1.4.5/pyramid/registry.py0000664000175000017500000002154512203712502016352 0ustar takakitakakiimport operator from zope.interface import implementer from zope.interface.registry import Components from pyramid.compat import text_ from pyramid.interfaces import ( ISettings, IIntrospector, IIntrospectable, ) empty = text_('') class Registry(Components, dict): """ A registry object is an :term:`application registry`. It is used by the framework itself to perform mappings of URLs to view callables, as well as servicing other various framework duties. A registry has its own internal API, but this API is rarely used by Pyramid application developers (it's usually only used by developers of the Pyramid framework). But it has a number of attributes that may be useful to application developers within application code, such as ``settings``, which is a dictionary containing application deployment settings. For information about the purpose and usage of the application registry, see :ref:`zca_chapter`. The application registry is usually accessed as ``request.registry`` in application code. """ # for optimization purposes, if no listeners are listening, don't try # to notify them has_listeners = False _settings = None def __nonzero__(self): # defeat bool determination via dict.__len__ return True def registerSubscriptionAdapter(self, *arg, **kw): result = Components.registerSubscriptionAdapter(self, *arg, **kw) self.has_listeners = True return result def registerSelfAdapter(self, required=None, provided=None, name=empty, info=empty, event=True): # registerAdapter analogue which always returns the object itself # when required is matched return self.registerAdapter(lambda x: x, required=required, provided=provided, name=name, info=info, event=event) def queryAdapterOrSelf(self, object, interface, default=None): # queryAdapter analogue which returns the object if it implements # the interface, otherwise it will return an adaptation to the # interface if not interface.providedBy(object): return self.queryAdapter(object, interface, default=default) return object def registerHandler(self, *arg, **kw): result = Components.registerHandler(self, *arg, **kw) self.has_listeners = True return result def notify(self, *events): if self.has_listeners: # iterating over subscribers assures they get executed [ _ for _ in self.subscribers(events, None) ] # backwards compatibility for code that wants to look up a settings # object via ``registry.getUtility(ISettings)`` def _get_settings(self): return self._settings def _set_settings(self, settings): self.registerUtility(settings, ISettings) self._settings = settings settings = property(_get_settings, _set_settings) @implementer(IIntrospector) class Introspector(object): def __init__(self): self._refs = {} self._categories = {} self._counter = 0 def add(self, intr): category = self._categories.setdefault(intr.category_name, {}) category[intr.discriminator] = intr category[intr.discriminator_hash] = intr intr.order = self._counter self._counter += 1 def get(self, category_name, discriminator, default=None): category = self._categories.setdefault(category_name, {}) intr = category.get(discriminator, default) return intr def get_category(self, category_name, default=None, sort_key=None): if sort_key is None: sort_key = operator.attrgetter('order') category = self._categories.get(category_name) if category is None: return default values = category.values() values = sorted(set(values), key=sort_key) return [ {'introspectable':intr, 'related':self.related(intr)} for intr in values ] def categorized(self, sort_key=None): L = [] for category_name in self.categories(): L.append((category_name, self.get_category(category_name, sort_key=sort_key))) return L def categories(self): return sorted(self._categories.keys()) def remove(self, category_name, discriminator): intr = self.get(category_name, discriminator) if intr is None: return L = self._refs.pop(intr, []) for d in L: L2 = self._refs[d] L2.remove(intr) category = self._categories[intr.category_name] del category[intr.discriminator] del category[intr.discriminator_hash] def _get_intrs_by_pairs(self, pairs): introspectables = [] for pair in pairs: category_name, discriminator = pair intr = self._categories.get(category_name, {}).get(discriminator) if intr is None: raise KeyError((category_name, discriminator)) introspectables.append(intr) return introspectables def relate(self, *pairs): introspectables = self._get_intrs_by_pairs(pairs) relatable = ((x,y) for x in introspectables for y in introspectables) for x, y in relatable: L = self._refs.setdefault(x, []) if x is not y and y not in L: L.append(y) def unrelate(self, *pairs): introspectables = self._get_intrs_by_pairs(pairs) relatable = ((x,y) for x in introspectables for y in introspectables) for x, y in relatable: L = self._refs.get(x, []) if y in L: L.remove(y) def related(self, intr): category_name, discriminator = intr.category_name, intr.discriminator intr = self._categories.get(category_name, {}).get(discriminator) if intr is None: raise KeyError((category_name, discriminator)) return self._refs.get(intr, []) @implementer(IIntrospectable) class Introspectable(dict): order = 0 # mutated by introspector.add action_info = None # mutated by self.register def __init__(self, category_name, discriminator, title, type_name): self.category_name = category_name self.discriminator = discriminator self.title = title self.type_name = type_name self._relations = [] def relate(self, category_name, discriminator): self._relations.append((True, category_name, discriminator)) def unrelate(self, category_name, discriminator): self._relations.append((False, category_name, discriminator)) def _assert_resolved(self): assert undefer(self.discriminator) is self.discriminator @property def discriminator_hash(self): self._assert_resolved() return hash(self.discriminator) def __hash__(self): self._assert_resolved() return hash((self.category_name,) + (self.discriminator,)) def __repr__(self): self._assert_resolved() return '<%s category %r, discriminator %r>' % (self.__class__.__name__, self.category_name, self.discriminator) def __nonzero__(self): return True __bool__ = __nonzero__ # py3 def register(self, introspector, action_info): self.discriminator = undefer(self.discriminator) self.action_info = action_info introspector.add(self) for relate, category_name, discriminator in self._relations: discriminator = undefer(discriminator) if relate: method = introspector.relate else: method = introspector.unrelate method( (self.category_name, self.discriminator), (category_name, discriminator) ) class Deferred(object): """ Can be used by a third-party configuration extender to wrap a :term:`discriminator` during configuration if an immediately hashable discriminator cannot be computed because it relies on unresolved values. The function should accept no arguments and should return a hashable discriminator.""" def __init__(self, func): self.func = func def resolve(self): return self.func() def undefer(v): """ Function which accepts an object and returns it unless it is a :class:`pyramid.registry.Deferred` instance. If it is an instance of that class, its ``resolve`` method is called, and the result of the method is returned.""" if isinstance(v, Deferred): v = v.resolve() return v class predvalseq(tuple): """ A subtype of tuple used to represent a sequence of predicate values """ pass global_registry = Registry('global') pyramid-1.4.5/pyramid/session.py0000664000175000017500000002517612210154276016177 0ustar takakitakakifrom hashlib import sha1 import base64 import binascii import hmac import time import os from zope.interface import implementer from pyramid.compat import ( pickle, PY3, text_, bytes_, native_, ) from pyramid.httpexceptions import HTTPBadRequest from pyramid.interfaces import ISession from pyramid.util import strings_differ def manage_accessed(wrapped): """ Decorator which causes a cookie to be set when a wrapped method is called""" def accessed(session, *arg, **kw): session.accessed = int(time.time()) if not session._dirty: session._dirty = True def set_cookie_callback(request, response): session._set_cookie(response) session.request = None # explicitly break cycle for gc session.request.add_response_callback(set_cookie_callback) return wrapped(session, *arg, **kw) accessed.__doc__ = wrapped.__doc__ return accessed def signed_serialize(data, secret): """ Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest() return sig + native_(base64.b64encode(pickled)) def signed_deserialize(serialized, secret, hmac=hmac): """ Deserialize the value returned from ``signed_serialize``. If the value cannot be deserialized for any reason, a :exc:`ValueError` exception will be raised. This function is useful for deserializing a signed cookie value created by ``signed_serialize``. For example: .. code-block:: python cookieval = request.cookies['signed_cookie'] data = signed_deserialize(cookieval, 'secret') """ # hmac parameterized only for unit tests try: input_sig, pickled = (serialized[:40], base64.b64decode(bytes_(serialized[40:]))) except (binascii.Error, TypeError) as e: # Badly formed data can make base64 die raise ValueError('Badly formed base64 data: %s' % e) sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest() # Avoid timing attacks (see # http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf) if strings_differ(sig, input_sig): raise ValueError('Invalid signature') return pickle.loads(pickled) def check_csrf_token(request, token='csrf_token', raises=True): """ Check the CSRF token in the request's session against the value in ``request.params.get(token)``. If a ``token`` keyword is not supplied to this function, the string ``csrf_token`` will be used to look up the token within ``request.params``. If the value in ``request.params.get(token)`` doesn't match the value supplied by ``request.session.get_csrf_token()``, and ``raises`` is ``True``, this function will raise an :exc:`pyramid.httpexceptions.HTTPBadRequest` exception. If the check does succeed and ``raises`` is ``False``, this function will return ``False``. If the CSRF check is successful, this function will return ``True`` unconditionally. Note that using this function requires that a :term:`session factory` is configured. .. versionadded:: 1.4a2 """ if request.params.get(token) != request.session.get_csrf_token(): if raises: raise HTTPBadRequest('incorrect CSRF token') return False return True def UnencryptedCookieSessionFactoryConfig( secret, timeout=1200, cookie_name='session', cookie_max_age=None, cookie_path='/', cookie_domain=None, cookie_secure=False, cookie_httponly=False, cookie_on_exception=True, signed_serialize=signed_serialize, signed_deserialize=signed_deserialize, ): """ Configure a :term:`session factory` which will provide unencrypted (but signed) cookie-based sessions. The return value of this function is a :term:`session factory`, which may be provided as the ``session_factory`` argument of a :class:`pyramid.config.Configurator` constructor, or used as the ``session_factory`` argument of the :meth:`pyramid.config.Configurator.set_session_factory` method. The session factory returned by this function will create sessions which are limited to storing fewer than 4000 bytes of data (as the payload must fit into a single cookie). Parameters: ``secret`` A string which is used to sign the cookie. ``timeout`` A number of seconds of inactivity before a session times out. ``cookie_name`` The name of the cookie used for sessioning. Default: ``session``. ``cookie_max_age`` The maximum age of the cookie used for sessioning (in seconds). Default: ``None`` (browser scope). ``cookie_path`` The path used for the session cookie. Default: ``/``. ``cookie_domain`` The domain used for the session cookie. Default: ``None`` (no domain). ``cookie_secure`` The 'secure' flag of the session cookie. Default: ``False``. ``cookie_httponly`` The 'httpOnly' flag of the session cookie. Default: ``False``. ``cookie_on_exception`` If ``True``, set a session cookie even if an exception occurs while rendering a view. Default: ``True``. ``signed_serialize`` A callable which takes more or less arbitrary python data structure and a secret and returns a signed serialization in bytes. Default: ``signed_serialize`` (using pickle). ``signed_deserialize`` A callable which takes a signed and serialized data structure in bytes and a secret and returns the original data structure if the signature is valid. Default: ``signed_deserialize`` (using pickle). """ @implementer(ISession) class UnencryptedCookieSessionFactory(dict): """ Dictionary-like session object """ # configuration parameters _cookie_name = cookie_name _cookie_max_age = cookie_max_age _cookie_path = cookie_path _cookie_domain = cookie_domain _cookie_secure = cookie_secure _cookie_httponly = cookie_httponly _cookie_on_exception = cookie_on_exception _secret = secret _timeout = timeout # dirty flag _dirty = False def __init__(self, request): self.request = request now = time.time() created = accessed = now new = True value = None state = {} cookieval = request.cookies.get(self._cookie_name) if cookieval is not None: try: value = signed_deserialize(cookieval, self._secret) except ValueError: value = None if value is not None: accessed, created, state = value new = False if now - accessed > self._timeout: state = {} self.created = created self.accessed = accessed self.new = new dict.__init__(self, state) # ISession methods def changed(self): """ This is intentionally a noop; the session is serialized on every access, so unnecessary""" pass def invalidate(self): self.clear() # XXX probably needs to unset cookie # non-modifying dictionary methods get = manage_accessed(dict.get) __getitem__ = manage_accessed(dict.__getitem__) items = manage_accessed(dict.items) values = manage_accessed(dict.values) keys = manage_accessed(dict.keys) __contains__ = manage_accessed(dict.__contains__) __len__ = manage_accessed(dict.__len__) __iter__ = manage_accessed(dict.__iter__) if not PY3: iteritems = manage_accessed(dict.iteritems) itervalues = manage_accessed(dict.itervalues) iterkeys = manage_accessed(dict.iterkeys) has_key = manage_accessed(dict.has_key) # modifying dictionary methods clear = manage_accessed(dict.clear) update = manage_accessed(dict.update) setdefault = manage_accessed(dict.setdefault) pop = manage_accessed(dict.pop) popitem = manage_accessed(dict.popitem) __setitem__ = manage_accessed(dict.__setitem__) __delitem__ = manage_accessed(dict.__delitem__) # flash API methods @manage_accessed def flash(self, msg, queue='', allow_duplicate=True): storage = self.setdefault('_f_' + queue, []) if allow_duplicate or (msg not in storage): storage.append(msg) @manage_accessed def pop_flash(self, queue=''): storage = self.pop('_f_' + queue, []) return storage @manage_accessed def peek_flash(self, queue=''): storage = self.get('_f_' + queue, []) return storage # CSRF API methods @manage_accessed def new_csrf_token(self): token = text_(binascii.hexlify(os.urandom(20))) self['_csrft_'] = token return token @manage_accessed def get_csrf_token(self): token = self.get('_csrft_', None) if token is None: token = self.new_csrf_token() return token # non-API methods def _set_cookie(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False cookieval = signed_serialize( (self.accessed, self.created, dict(self)), self._secret ) if len(cookieval) > 4064: raise ValueError( 'Cookie value is too long to store (%s bytes)' % len(cookieval) ) response.set_cookie( self._cookie_name, value=cookieval, max_age = self._cookie_max_age, path = self._cookie_path, domain = self._cookie_domain, secure = self._cookie_secure, httponly = self._cookie_httponly, ) return True return UnencryptedCookieSessionFactory pyramid-1.4.5/pyramid/security.py0000664000175000017500000002653612203712502016356 0ustar takakitakakifrom zope.interface import providedBy from pyramid.interfaces import ( IAuthenticationPolicy, IAuthorizationPolicy, ISecuredView, IView, IViewClassifier, ) from pyramid.compat import map_ from pyramid.threadlocal import get_current_registry Everyone = 'system.Everyone' Authenticated = 'system.Authenticated' Allow = 'Allow' Deny = 'Deny' class AllPermissionsList(object): """ Stand in 'permission list' to represent all permissions """ def __iter__(self): return () def __contains__(self, other): return True def __eq__(self, other): return isinstance(other, self.__class__) ALL_PERMISSIONS = AllPermissionsList() DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) NO_PERMISSION_REQUIRED = '__no_permission_required__' def has_permission(permission, context, request): """ Provided a permission (a string or unicode object), a context (a :term:`resource` instance) and a request object, return an instance of :data:`pyramid.security.Allowed` if the permission is granted in this context to the user implied by the request. Return an instance of :mod:`pyramid.security.Denied` if this permission is not granted in this context to this user. This function delegates to the current authentication and authorization policies. Return :data:`pyramid.security.Allowed` unconditionally if no authentication policy has been configured in this application.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c authn_policy = reg.queryUtility(IAuthenticationPolicy) if authn_policy is None: return Allowed('No authentication policy in use.') authz_policy = reg.queryUtility(IAuthorizationPolicy) if authz_policy is None: raise ValueError('Authentication policy registered without ' 'authorization policy') # should never happen principals = authn_policy.effective_principals(request) return authz_policy.permits(context, principals, permission) def authenticated_userid(request): """ Return the userid of the currently authenticated user or ``None`` if there is no :term:`authentication policy` in effect or there is no currently authenticated user.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c policy = reg.queryUtility(IAuthenticationPolicy) if policy is None: return None return policy.authenticated_userid(request) def unauthenticated_userid(request): """ Return an object which represents the *claimed* (not verified) user id of the credentials present in the request. ``None`` if there is no :term:`authentication policy` in effect or there is no user data associated with the current request. This differs from :func:`~pyramid.security.authenticated_userid`, because the effective authentication policy will not ensure that a record associated with the userid exists in persistent storage.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c policy = reg.queryUtility(IAuthenticationPolicy) if policy is None: return None return policy.unauthenticated_userid(request) def effective_principals(request): """ Return the list of 'effective' :term:`principal` identifiers for the ``request``. This will include the userid of the currently authenticated user if a user is currently authenticated. If no :term:`authentication policy` is in effect, this will return an empty sequence.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c policy = reg.queryUtility(IAuthenticationPolicy) if policy is None: return [Everyone] return policy.effective_principals(request) def principals_allowed_by_permission(context, permission): """ Provided a ``context`` (a resource object), and a ``permission`` (a string or unicode object), if a :term:`authorization policy` is in effect, return a sequence of :term:`principal` ids that possess the permission in the ``context``. If no authorization policy is in effect, this will return a sequence with the single value :mod:`pyramid.security.Everyone` (the special principal identifier representing all principals). .. note:: even if an :term:`authorization policy` is in effect, some (exotic) authorization policies may not implement the required machinery for this function; those will cause a :exc:`NotImplementedError` exception to be raised when this function is invoked. """ reg = get_current_registry() policy = reg.queryUtility(IAuthorizationPolicy) if policy is None: return [Everyone] return policy.principals_allowed_by_permission(context, permission) def view_execution_permitted(context, request, name=''): """ If the view specified by ``context`` and ``name`` is protected by a :term:`permission`, check the permission associated with the view using the effective authentication/authorization policies and the ``request``. Return a boolean result. If no :term:`authorization policy` is in effect, or if the view is not protected by a permission, return ``True``. If no view can view found, an exception will be raised. .. versionchanged:: 1.4a4 An exception is raised if no view is found. """ try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c provides = [IViewClassifier] + map_(providedBy, (request, context)) view = reg.adapters.lookup(provides, ISecuredView, name=name) if view is None: view = reg.adapters.lookup(provides, IView, name=name) if view is None: raise TypeError('No registered view satisfies the constraints. ' 'It would not make sense to claim that this view ' '"is" or "is not" permitted.') return Allowed( 'Allowed: view name %r in context %r (no permission defined)' % (name, context)) return view.__permitted__(context, request) def remember(request, principal, **kw): """ Return a sequence of header tuples (e.g. ``[('Set-Cookie', 'foo=abc')]``) suitable for 'remembering' a set of credentials implied by the data passed as ``principal`` and ``*kw`` using the current :term:`authentication policy`. Common usage might look like so within the body of a view function (``response`` is assumed to be a :term:`WebOb` -style :term:`response` object computed previously by the view code):: from pyramid.security import remember headers = remember(request, 'chrism', password='123', max_age='86400') response.headerlist.extend(headers) return response If no :term:`authentication policy` is in use, this function will always return an empty sequence. If used, the composition and meaning of ``**kw`` must be agreed upon by the calling code and the effective authentication policy.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c policy = reg.queryUtility(IAuthenticationPolicy) if policy is None: return [] else: return policy.remember(request, principal, **kw) def forget(request): """ Return a sequence of header tuples (e.g. ``[('Set-Cookie', 'foo=abc')]``) suitable for 'forgetting' the set of credentials possessed by the currently authenticated user. A common usage might look like so within the body of a view function (``response`` is assumed to be an :term:`WebOb` -style :term:`response` object computed previously by the view code):: from pyramid.security import forget headers = forget(request) response.headerlist.extend(headers) return response If no :term:`authentication policy` is in use, this function will always return an empty sequence.""" try: reg = request.registry except AttributeError: reg = get_current_registry() # b/c policy = reg.queryUtility(IAuthenticationPolicy) if policy is None: return [] else: return policy.forget(request) class PermitsResult(int): def __new__(cls, s, *args): inst = int.__new__(cls, cls.boolval) inst.s = s inst.args = args return inst @property def msg(self): return self.s % self.args def __str__(self): return self.msg def __repr__(self): return '<%s instance at %s with msg %r>' % (self.__class__.__name__, id(self), self.msg) class Denied(PermitsResult): """ An instance of ``Denied`` is returned when a security-related API or other :app:`Pyramid` code denies an action unrelated to an ACL check. It evaluates equal to all boolean false types. It has an attribute named ``msg`` describing the circumstances for the deny.""" boolval = 0 class Allowed(PermitsResult): """ An instance of ``Allowed`` is returned when a security-related API or other :app:`Pyramid` code allows an action unrelated to an ACL check. It evaluates equal to all boolean true types. It has an attribute named ``msg`` describing the circumstances for the allow.""" boolval = 1 class ACLPermitsResult(int): def __new__(cls, ace, acl, permission, principals, context): inst = int.__new__(cls, cls.boolval) inst.permission = permission inst.ace = ace inst.acl = acl inst.principals = principals inst.context = context return inst @property def msg(self): s = ('%s permission %r via ACE %r in ACL %r on context %r for ' 'principals %r') return s % (self.__class__.__name__, self.permission, self.ace, self.acl, self.context, self.principals) def __str__(self): return self.msg def __repr__(self): return '<%s instance at %s with msg %r>' % (self.__class__.__name__, id(self), self.msg) class ACLDenied(ACLPermitsResult): """ An instance of ``ACLDenied`` represents that a security check made explicitly against ACL was denied. It evaluates equal to all boolean false types. It also has the following attributes: ``acl``, ``ace``, ``permission``, ``principals``, and ``context``. These attributes indicate the security values involved in the request. Its __str__ method prints a summary of these attributes for debugging purposes. The same summary is available as the ``msg`` attribute.""" boolval = 0 class ACLAllowed(ACLPermitsResult): """ An instance of ``ACLAllowed`` represents that a security check made explicitly against ACL was allowed. It evaluates equal to all boolean true types. It also has the following attributes: ``acl``, ``ace``, ``permission``, ``principals``, and ``context``. These attributes indicate the security values involved in the request. Its __str__ method prints a summary of these attributes for debugging purposes. The same summary is available as the ``msg`` attribute.""" boolval = 1 pyramid-1.4.5/pyramid/settings.py0000664000175000017500000000217212203712502016335 0ustar takakitakakifrom pyramid.compat import string_types truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) def asbool(s): """ Return the boolean value ``True`` if the case-lowered value of string input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise return the boolean value ``False``. If ``s`` is the value ``None``, return ``False``. If ``s`` is already one of the boolean values ``True`` or ``False``, return it.""" if s is None: return False if isinstance(s, bool): return s s = str(s).strip() return s.lower() in truthy def aslist_cronly(value): if isinstance(value, string_types): value = filter(None, [x.strip() for x in value.splitlines()]) return list(value) def aslist(value, flatten=True): """ Return a list of strings, separating the input based on newlines and, if flatten=True (the default), also split on spaces within each line.""" values = aslist_cronly(value) if not flatten: return values result = [] for value in values: subvalues = value.split() result.extend(subvalues) return result pyramid-1.4.5/setup.py0000664000175000017500000001022112210155664014172 0ustar takakitakaki############################################################################## # # Copyright (c) 2008-2011 Agendaless Consulting and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the BSD-like license at # http://www.repoze.org/LICENSE.txt. A copy of the license should accompany # this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL # EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND # FITNESS FOR A PARTICULAR PURPOSE # ############################################################################## import os import sys from setuptools import setup, find_packages py_version = sys.version_info[:2] PY3 = py_version[0] == 3 if PY3: if py_version < (3, 2): raise RuntimeError('On Python 3, Pyramid requires Python 3.2 or better') else: if py_version < (2, 6): raise RuntimeError('On Python 2, Pyramid requires Python 2.6 or better') here = os.path.abspath(os.path.dirname(__file__)) try: README = open(os.path.join(here, 'README.rst')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() except IOError: README = CHANGES = '' install_requires=[ 'setuptools', 'Chameleon >= 1.2.3', 'Mako >= 0.3.6', # strict_undefined 'WebOb >= 1.2b3', # request.path_info is unicode 'repoze.lru >= 0.4', # py3 compat 'zope.interface >= 3.8.0', # has zope.interface.registry 'zope.deprecation >= 3.5.0', # py3 compat 'venusian >= 1.0a3', # ``ignore`` 'translationstring >= 0.4', # py3 compat 'PasteDeploy >= 1.5.0', # py3 compat ] tests_require = [ 'WebTest >= 1.3.1', # py3 compat ] if not PY3: tests_require.append('zope.component>=3.11.0') docs_extras = [ 'Sphinx', 'docutils', 'repoze.sphinx.autointerface', ] testing_extras = tests_require + [ 'nose', 'coverage', 'virtualenv', # for scaffolding tests ] setup(name='pyramid', version='1.4.5', description=('The Pyramid web application development framework, a ' 'Pylons project'), long_description=README + '\n\n' + CHANGES, classifiers=[ "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI", "License :: Repoze Public License", ], keywords='web wsgi pylons pyramid', author="Chris McDonough, Agendaless Consulting", author_email="pylons-discuss@googlegroups.com", url="http://pylonsproject.org", license="BSD-derived (http://www.repoze.org/LICENSE.txt)", packages=find_packages(), include_package_data=True, zip_safe=False, install_requires = install_requires, extras_require = { 'testing':testing_extras, 'docs':docs_extras, }, tests_require = tests_require, test_suite="pyramid.tests", entry_points = """\ [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/HISTORY.txt0000664000175000017500000040447312210154276014400 0ustar takakitakaki1.3 (2012-03-21) ================ Bug Fixes --------- - When ``pyramid.wsgi.wsgiapp2`` calls the downstream WSGI app, the app's environ will no longer have (deprecated and potentially misleading) ``bfg.routes.matchdict`` or ``bfg.routes.route`` keys in it. A symptom of this bug would be a ``wsgiapp2``-wrapped Pyramid app finding the wrong view because it mistakenly detects that a route was matched when, in fact, it was not. - The fix for issue https://github.com/Pylons/pyramid/issues/461 (which made it possible for instance methods to be used as view callables) introduced a backwards incompatibility when methods that declared only a request argument were used. See https://github.com/Pylons/pyramid/issues/503 1.3b3 (2012-03-17) ================== Bug Fixes --------- - ``config.add_view()`` raised AttributeError involving ``__text__``. See https://github.com/Pylons/pyramid/issues/461 - Remove references to do-nothing ``pyramid.debug_templates`` setting in all Pyramid-provided ``.ini`` files. This setting previously told Chameleon to render better exceptions; now Chameleon always renders nice exceptions regardless of the value of this setting. Scaffolds --------- - The ``alchemy`` scaffold now shows an informative error message in the browser if the person creating the project forgets to run the initialization script. - The ``alchemy`` scaffold initialization script is now called ``initialize__db`` instead of ``populate_``. Documentation ------------- - Wiki tutorials improved due to collaboration at PyCon US 2012 sprints. 1.3b2 (2012-03-02) ================== Bug Fixes --------- - The method ``pyramid.request.Request.partial_application_url`` is no longer in the API docs. It was meant to be a private method; its publication in the documentation as an API method was a mistake, and it has been renamed to something private. - When a static view was registered using an absolute filesystem path on Windows, the ``request.static_url`` function did not work to generate URLs to its resources. Symptom: "No static URL definition matching c:\\foo\\bar\\baz". - Make all tests pass on Windows XP. - Bug in ACL authentication checking on Python 3: the ``permits`` and ``principals_allowed_by_permission`` method of ``pyramid.authorization.ACLAuthenticationPolicy`` could return an inappropriate ``True`` value when a permission on an ACL was a string rather than a sequence, and then only if the ACL permission string was a substring of the ``permission`` value passed to the function. This bug effects no Pyramid deployment under Python 2; it is a bug that exists only in deployments running on Python 3. It has existed since Pyramid 1.3a1. This bug was due to the presence of an ``__iter__`` attribute on strings under Python 3 which is not present under strings in Python 2. 1.3b1 (2012-02-26) ================== Bug Fixes --------- - ``pyramid.config.Configurator.with_package`` didn't work if the Configurator was an old-style ``pyramid.configuration.Configurator`` instance. - Pyramid authorization policies did not show up in the introspector. Deprecations ------------ - All references to the ``tmpl_context`` request variable were removed from the docs. Its existence in Pyramid is confusing for people who were never Pylons users. It was added as a porting convenience for Pylons users in Pyramid 1.0, but it never caught on because the Pyramid rendering system is a lot different than Pylons' was, and alternate ways exist to do what it was designed to offer in Pylons. It will continue to exist "forever" but it will not be recommended or mentioned in the docs. 1.3a9 (2012-02-22) ================== Features -------- - Add an ``introspection`` boolean to the Configurator constructor. If this is ``True``, actions registered using the Configurator will be registered with the introspector. If it is ``False``, they won't. The default is ``True``. Setting it to ``False`` during action processing will prevent introspection for any following registration statements, and setting it to ``True`` will start them up again. This addition is to service a requirement that the debug toolbar's own views and methods not show up in the introspector. - New API: ``pyramid.config.Configurator.add_notfound_view``. This is a wrapper for ``pyramid.Config.configurator.add_view`` which provides easy append_slash support and does the right thing about permissions. It should be preferred over calling ``add_view`` directly with ``context=HTTPNotFound`` as was previously recommended. - New API: ``pyramid.view.notfound_view_config``. This is a decorator constructor like ``pyramid.view.view_config`` that calls ``pyramid.config.Configurator.add_notfound_view`` when scanned. It should be preferred over using ``pyramid.view.view_config`` with ``context=HTTPNotFound`` as was previously recommended. - New API: ``pyramid.config.Configurator.add_forbidden_view``. This is a wrapper for ``pyramid.Config.configurator.add_view`` which does the right thing about permissions. It should be preferred over calling ``add_view`` directly with ``context=HTTPForbidden`` as was previously recommended. - New API: ``pyramid.view.forbidden_view_config``. This is a decorator constructor like ``pyramid.view.view_config`` that calls ``pyramid.config.Configurator.add_forbidden_view`` when scanned. It should be preferred over using ``pyramid.view.view_config`` with ``context=HTTPForbidden`` as was previously recommended. - New APIs: ``pyramid.response.FileResponse`` and ``pyramid.response.FileIter``, for usage in views that must serve files "manually". Backwards Incompatibilities --------------------------- - Remove ``pyramid.config.Configurator.with_context`` class method. It was never an API, it is only used by ``pyramid_zcml`` and its functionality has been moved to that package's latest release. This means that you'll need to use the 0.9.2 or later release of ``pyramid_zcml`` with this release of Pyramid. - The ``introspector`` argument to the ``pyramid.config.Configurator`` constructor API has been removed. It has been replaced by the boolean ``introspection`` flag. - The ``pyramid.registry.noop_introspector`` API object has been removed. - The older deprecated ``set_notfound_view`` Configurator method is now an alias for the new ``add_notfound_view`` Configurator method. Likewise, the older deprecated ``set_forbidden_view`` is now an alias for the new ``add_forbidden_view``. This has the following impact: the ``context`` sent to views with a ``(context, request)`` call signature registered via the ``set_notfound_view`` or ``set_forbidden_view`` will now be an exception object instead of the actual resource context found. Use ``request.context`` to get the actual resource context. It's also recommended to disuse ``set_notfound_view`` in favor of ``add_notfound_view``, and disuse ``set_forbidden_view`` in favor of ``add_forbidden_view`` despite the aliasing. Deprecations ------------ - The API documentation for ``pyramid.view.append_slash_notfound_view`` and ``pyramid.view.AppendSlashNotFoundViewFactory`` was removed. These names still exist and are still importable, but they are no longer APIs. Use ``pyramid.config.Configurator.add_notfound_view(append_slash=True)`` or ``pyramid.view.notfound_view_config(append_slash=True)`` to get the same behavior. - The ``set_forbidden_view`` and ``set_notfound_view`` methods of the Configurator were removed from the documentation. They have been deprecated since Pyramid 1.1. Bug Fixes --------- - The static file response object used by ``config.add_static_view`` opened the static file twice, when it only needed to open it once. - The AppendSlashNotFoundViewFactory used request.path to match routes. This was wrong because request.path contains the script name, and this would cause it to fail in circumstances where the script name was not empty. It should have used request.path_info, and now does. Documentation ------------- - Updated the "Creating a Not Found View" section of the "Hooks" chapter, replacing explanations of registering a view using ``add_view`` or ``view_config`` with ones using ``add_notfound_view`` or ``notfound_view_config``. - Updated the "Creating a Not Forbidden View" section of the "Hooks" chapter, replacing explanations of registering a view using ``add_view`` or ``view_config`` with ones using ``add_forbidden_view`` or ``forbidden_view_config``. - Updated the "Redirecting to Slash-Appended Routes" section of the "URL Dispatch" chapter, replacing explanations of registering a view using ``add_view`` or ``view_config`` with ones using ``add_notfound_view`` or ``notfound_view_config`` - Updated all tutorials to use ``pyramid.view.forbidden_view_config`` rather than ``pyramid.view.view_config`` with an HTTPForbidden context. 1.3a8 (2012-02-19) ================== Features -------- - The ``scan`` method of a ``Configurator`` can be passed an ``ignore`` argument, which can be a string, a callable, or a list consisting of strings and/or callables. This feature allows submodules, subpackages, and global objects from being scanned. See http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for more information about how to use the ``ignore`` argument to ``scan``. - Better error messages when a view callable returns a value that cannot be converted to a response (for example, when a view callable returns a dictionary without a renderer defined, or doesn't return any value at all). The error message now contains information about the view callable itself as well as the result of calling it. - Better error message when a .pyc-only module is ``config.include`` -ed. This is not permitted due to error reporting requirements, and a better error message is shown when it is attempted. Previously it would fail with something like "AttributeError: 'NoneType' object has no attribute 'rfind'". - Add ``pyramid.config.Configurator.add_traverser`` API method. See the Hooks narrative documentation section entitled "Changing the Traverser" for more information. This is not a new feature, it just provides an API for adding a traverser without needing to use the ZCA API. - Add ``pyramid.config.Configurator.add_resource_url_adapter`` API method. See the Hooks narrative documentation section entitled "Changing How pyramid.request.Request.resource_url Generates a URL" for more information. This is not a new feature, it just provides an API for adding a resource url adapter without needing to use the ZCA API. - The system value ``req`` is now supplied to renderers as an alias for ``request``. This means that you can now, for example, in a template, do ``req.route_url(...)`` instead of ``request.route_url(...)``. This is purely a change to reduce the amount of typing required to use request methods and attributes from within templates. The value ``request`` is still available too, this is just an alternative. - A new interface was added: ``pyramid.interfaces.IResourceURL``. An adapter implementing its interface can be used to override resource URL generation when ``request.resource_url`` is called. This interface replaces the now-deprecated ``pyramid.interfaces.IContextURL`` interface. - The dictionary passed to a resource's ``__resource_url__`` method (see "Overriding Resource URL Generation" in the "Resources" chapter) now contains an ``app_url`` key, representing the application URL generated during ``request.resource_url``. It represents a potentially customized URL prefix, containing potentially custom scheme, host and port information passed by the user to ``request.resource_url``. It should be used instead of ``request.application_url`` where necessary. - The ``request.resource_url`` API now accepts these arguments: ``app_url``, ``scheme``, ``host``, and ``port``. The app_url argument can be used to replace the URL prefix wholesale during url generation. The ``scheme``, ``host``, and ``port`` arguments can be used to replace the respective default values of ``request.application_url`` partially. - A new API named ``request.resource_path`` now exists. It works like ``request.resource_url`` but produces a relative URL rather than an absolute one. - The ``request.route_url`` API now accepts these arguments: ``_app_url``, ``_scheme``, ``_host``, and ``_port``. The ``_app_url`` argument can be used to replace the URL prefix wholesale during url generation. The ``_scheme``, ``_host``, and ``_port`` arguments can be used to replace the respective default values of ``request.application_url`` partially. Backwards Incompatibilities --------------------------- - The ``pyramid.interfaces.IContextURL`` interface has been deprecated. People have been instructed to use this to register a resource url adapter in the "Hooks" chapter to use to influence ``request.resource_url`` URL generation for resources found via custom traversers since Pyramid 1.0. The interface still exists and registering such an adapter still works, but this interface will be removed from the software after a few major Pyramid releases. You should replace it with an equivalent ``pyramid.interfaces.IResourceURL`` adapter, registered using the new ``pyramid.config.Configurator.add_resource_url_adapter`` API. A deprecation warning is now emitted when a ``pyramid.interfaces.IContextURL`` adapter is found when ``request.resource_url`` is called. Documentation ------------- - Don't create a ``session`` instance in SQLA Wiki tutorial, use raw ``DBSession`` instead (this is more common in real SQLA apps). Scaffolding ----------- - Put ``pyramid.includes`` targets within ini files in scaffolds on separate lines in order to be able to tell people to comment out only the ``pyramid_debugtoolbar`` line when they want to disable the toolbar. Dependencies ------------ - Depend on ``venusian`` >= 1.0a3 to provide scan ``ignore`` support. Internal -------- - Create a "MakoRendererFactoryHelper" that provides customizable settings key prefixes. Allows settings prefixes other than "mako." to be used to create different factories that don't use the global mako settings. This will be useful for the debug toolbar, which can currently be sabotaged by someone using custom mako configuration settings. 1.3a7 (2012-02-07) ================== Features -------- - More informative error message when a ``config.include`` cannot find an ``includeme``. See https://github.com/Pylons/pyramid/pull/392. - Internal: catch unhashable discriminators early (raise an error instead of allowing them to find their way into resolveConflicts). - The `match_param` view predicate now accepts a string or a tuple. This replaces the broken behavior of accepting a dict. See https://github.com/Pylons/pyramid/issues/425 for more information. Bug Fixes --------- - The process will now restart when ``pserve`` is used with the ``--reload`` flag when the ``development.ini`` file (or any other .ini file in use) is changed. See https://github.com/Pylons/pyramid/issues/377 and https://github.com/Pylons/pyramid/pull/411 - The ``prequest`` script would fail when used against URLs which did not return HTML or text. See https://github.com/Pylons/pyramid/issues/381 Backwards Incompatibilities --------------------------- - The `match_param` view predicate no longer accepts a dict. This will have no negative affect because the implementation was broken for dict-based arguments. Documentation ------------- - Add a traversal hello world example to the narrative docs. 1.3a6 (2012-01-20) ================== Features -------- - New API: ``pyramid.config.Configurator.set_request_property``. Add lazy property descriptors to a request without changing the request factory. This method provides conflict detection and is the suggested way to add properties to a request. - Responses generated by Pyramid's ``static_view`` now use a ``wsgi.file_wrapper`` (see http://www.python.org/dev/peps/pep-0333/#optional-platform-specific-file-handling) when one is provided by the web server. Bug Fixes --------- - Views registered with an ``accept`` could not be overridden correctly with a different view that had the same predicate arguments. See https://github.com/Pylons/pyramid/pull/404 for more information. - When using a dotted name for a ``view`` argument to ``Configurator.add_view`` that pointed to a class with a ``view_defaults`` decorator, the view defaults would not be applied. See https://github.com/Pylons/pyramid/issues/396 . - Static URL paths were URL-quoted twice. See https://github.com/Pylons/pyramid/issues/407 . 1.3a5 (2012-01-09) ================== Bug Fixes --------- - The ``pyramid.view.view_defaults`` decorator did not work properly when more than one view relied on the defaults being different for configuration conflict resolution. See https://github.com/Pylons/pyramid/issues/394. Backwards Incompatibilities --------------------------- - The ``path_info`` route and view predicates now match against ``request.upath_info`` (Unicode) rather than ``request.path_info`` (indeterminate value based on Python 3 vs. Python 2). This has to be done to normalize matching on Python 2 and Python 3. 1.3a4 (2012-01-05) ================== Features -------- - New API: ``pyramid.request.Request.set_property``. Add lazy property descriptors to a request without changing the request factory. New properties may be reified, effectively caching the value for the lifetime of the instance. Common use-cases for this would be to get a database connection for the request or identify the current user. - Use the ``waitress`` WSGI server instead of ``wsgiref`` in scaffolding. Bug Fixes --------- - The documentation of ``pyramid.events.subscriber`` indicated that using it as a decorator with no arguments like this:: @subscriber() def somefunc(event): pass Would register ``somefunc`` to receive all events sent via the registry, but this was untrue. Instead, it would receive no events at all. This has now been fixed and the code matches the documentation. See also https://github.com/Pylons/pyramid/issues/386 - Literal portions of route patterns were not URL-quoted when ``route_url`` or ``route_path`` was used to generate a URL or path. - The result of ``route_path`` or ``route_url`` might have been ``unicode`` or ``str`` depending on the input. It is now guaranteed to always be ``str``. - URL matching when the pattern contained non-ASCII characters in literal parts was indeterminate. Now the pattern supplied to ``add_route`` is assumed to be either: a ``unicode`` value, or a ``str`` value that contains only ASCII characters. If you now want to match the path info from a URL that contains high order characters, you can pass the Unicode representation of the decoded path portion in the pattern. - When using a ``traverse=`` route predicate, traversal would fail with a URLDecodeError if there were any high-order characters in the traversal pattern or in the matched dynamic segments. - Using a dynamic segment named ``traverse`` in a route pattern like this:: config.add_route('trav_route', 'traversal/{traverse:.*}') Would cause a ``UnicodeDecodeError`` when the route was matched and the matched portion of the URL contained any high-order characters. See https://github.com/Pylons/pyramid/issues/385 . - When using a ``*traverse`` stararg in a route pattern, a URL that matched that possessed a ``@@`` in its name (signifying a view name) would be inappropriately quoted by the traversal machinery during traversal, resulting in the view not being found properly. See https://github.com/Pylons/pyramid/issues/382 and https://github.com/Pylons/pyramid/issues/375 . Backwards Incompatibilities --------------------------- - String values passed to ``route_url`` or ``route_path`` that are meant to replace "remainder" matches will now be URL-quoted except for embedded slashes. For example:: config.add_route('remain', '/foo*remainder') request.route_path('remain', remainder='abc / def') # -> '/foo/abc%20/%20def' Previously string values passed as remainder replacements were tacked on untouched, without any URL-quoting. But this doesn't really work logically if the value passed is Unicode (raw unicode cannot be placed in a URL or in a path) and it is inconsistent with the rest of the URL generation machinery if the value is a string (it won't be quoted unless by the caller). Some folks will have been relying on the older behavior to tack on query string elements and anchor portions of the URL; sorry, you'll need to change your code to use the ``_query`` and/or ``_anchor`` arguments to ``route_path`` or ``route_url`` to do this now. - If you pass a bytestring that contains non-ASCII characters to ``add_route`` as a pattern, it will now fail at startup time. Use Unicode instead. 1.3a3 (2011-12-21) ================== Features -------- - Added a ``prequest`` script (along the lines of ``paster request``). It is documented in the "Command-Line Pyramid" chapter in the section entitled "Invoking a Request". - Add undocumented ``__discriminator__`` API to derived view callables. e.g. ``adapters.lookup(...).__discriminator__(context, request)``. It will be used by superdynamic systems that require the discriminator to be used for introspection after manual view lookup. Bug Fixes --------- - Normalized exit values and ``-h`` output for all ``p*`` scripts (``pviews``, ``proutes``, etc). Documentation ------------- - Added a section named "Making Your Script into a Console Script" in the "Command-Line Pyramid" chapter. - Removed the "Running Pyramid on Google App Engine" tutorial from the main docs. It survives on in the Cookbook (http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/gae.html). Rationale: it provides the correct info for the Python 2.5 version of GAE only, and this version of Pyramid does not support Python 2.5. 1.3a2 (2011-12-14) ================== Features -------- - New API: ``pyramid.view.view_defaults``. If you use a class as a view, you can use the new ``view_defaults`` class decorator on the class to provide defaults to the view configuration information used by every ``@view_config`` decorator that decorates a method of that class. It also works against view configurations involving a class made imperatively. - Added a backwards compatibility knob to ``pcreate`` to emulate ``paster create`` handling for the ``--list-templates`` option. - Changed scaffolding machinery around a bit to make it easier for people who want to have extension scaffolds that can work across Pyramid 1.0.X, 1.1.X, 1.2.X and 1.3.X. See the new "Creating Pyramid Scaffolds" chapter in the narrative documentation for more info. Documentation ------------- - Added documentation to "View Configuration" narrative documentation chapter about ``view_defaults`` class decorator. - Added API docs for ``view_defaults`` class decorator. - Added an API docs chapter for ``pyramid.scaffolds``. - Added a narrative docs chapter named "Creating Pyramid Scaffolds". Backwards Incompatibilities --------------------------- - The ``template_renderer`` method of ``pyramid.scaffolds.PyramidScaffold`` was renamed to ``render_template``. If you were overriding it, you're a bad person, because it wasn't an API before now. But we're nice so we're letting you know. 1.3a1 (2011-12-09) ================== Features -------- - Python 3.2 compatibility. - New ``pyramid.compat`` module and API documentation which provides Python 2/3 straddling support for Pyramid add-ons and development environments. - A ``mako.directories`` setting is no longer required to use Mako templates Rationale: Mako template renderers can be specified using an absolute asset spec. An entire application can be written with such asset specs, requiring no ordered lookup path. - ``bpython`` interpreter compatibility in ``pshell``. See the "Command-Line Pyramid" narrative docs chapter for more information. - Added ``get_appsettings`` API function to the ``pyramid.paster`` module. This function returns the settings defined within an ``[app:...]`` section in a PasteDeploy ini file. - Added ``setup_logging`` API function to the ``pyramid.paster`` module. This function sets up Python logging according to the logging configuration in a PasteDeploy ini file. - Configuration conflict reporting is reported in a more understandable way ("Line 11 in file..." vs. a repr of a tuple of similar info). - A configuration introspection system was added; see the narrative documentation chapter entitled "Pyramid Configuration Introspection" for more information. New APIs: ``pyramid.registry.Introspectable``, ``pyramid.config.Configurator.introspector``, ``pyramid.config.Configurator.introspectable``, ``pyramid.registry.Registry.introspector``. - Allow extra keyword arguments to be passed to the ``pyramid.config.Configurator.action`` method. - New APIs: ``pyramid.path.AssetResolver`` and ``pyramid.path.DottedNameResolver``. The former can be used to resolve asset specifications, the latter can be used to resolve dotted names to modules or packages. Bug Fixes --------- - Make test suite pass on 32-bit systems; closes #286. closes #306. See also https://github.com/Pylons/pyramid/issues/286 - The ``pryamid.view.view_config`` decorator did not accept a ``match_params`` predicate argument. See https://github.com/Pylons/pyramid/pull/308 - The AuthTktCookieHelper could potentially generate Unicode headers inappropriately when the ``tokens`` argument to remember was used. See https://github.com/Pylons/pyramid/pull/314. - The AuthTktAuthenticationPolicy did not use a timing-attack-aware string comparator. See https://github.com/Pylons/pyramid/pull/320 for more info. - The DummySession in ``pyramid.testing`` now generates a new CSRF token if one doesn't yet exist. - ``request.static_url`` now generates URL-quoted URLs when fed a ``path`` argument which contains characters that are unsuitable for URLs. See https://github.com/Pylons/pyramid/issues/349 for more info. - Prevent a scaffold rendering from being named ``site`` (conflicts with Python internal site.py). - Support for using instances as targets of the ``pyramid.wsgi.wsgiapp`` and ``pryramid.wsgi.wsgiapp2`` functions. See https://github.com/Pylons/pyramid/pull/370 for more info. Backwards Incompatibilities --------------------------- - Pyramid no longer runs on Python 2.5 (which includes the most recent release of Jython and the Python 2.5 version of GAE as of this writing). - The ``paster`` command is no longer the documented way to create projects, start the server, or run debugging commands. To create projects from scaffolds, ``paster create`` is replaced by the ``pcreate`` console script. To serve up a project, ``paster serve`` is replaced by the ``pserve`` console script. New console scripts named ``pshell``, ``pviews``, ``proutes``, and ``ptweens`` do what their ``paster `` equivalents used to do. Rationale: the Paste and PasteScript packages do not run under Python 3. - The default WSGI server run as the result of ``pserve`` from newly rendered scaffolding is now the ``wsgiref`` WSGI server instead of the ``paste.httpserver`` server. Rationale: Rationale: the Paste and PasteScript packages do not run under Python 3. - The ``pshell`` command (see "paster pshell") no longer accepts a ``--disable-ipython`` command-line argument. Instead, it accepts a ``-p`` or ``--python-shell`` argument, which can be any of the values ``python``, ``ipython`` or ``bpython``. - Removed the ``pyramid.renderers.renderer_from_name`` function. It has been deprecated since Pyramid 1.0, and was never an API. - To use ZCML with versions of Pyramid >= 1.3, you will need ``pyramid_zcml`` version >= 0.8 and ``zope.configuration`` version >= 3.8.0. The ``pyramid_zcml`` package version 0.8 is backwards compatible all the way to Pyramid 1.0, so you won't be warned if you have older versions installed and upgrade Pyramid "in-place"; it may simply break instead. Dependencies ------------ - Pyramid no longer depends on the ``zope.component`` package, except as a testing dependency. - Pyramid now depends on a zope.interface>=3.8.0, WebOb>=1.2dev, repoze.lru>=0.4, zope.deprecation>=3.5.0, translationstring>=0.4 (for Python 3 compatibility purposes). It also, as a testing dependency, depends on WebTest>=1.3.1 for the same reason. - Pyramid no longer depends on the Paste or PasteScript packages. Documentation ------------- - The SQLAlchemy Wiki tutorial has been updated. It now uses ``@view_config`` decorators and an explicit database population script. - Minor updates to the ZODB Wiki tutorial. - A narrative documentation chapter named "Extending Pyramid Configuration" was added; it describes how to add a new directive, and how use the ``pyramid.config.Configurator.action`` method within custom directives. It also describes how to add introspectable objects. - A narrative documentation chapter named "Pyramid Configuration Introspection" was added. It describes how to query the introspection system. Scaffolds --------- - Rendered scaffolds have now been changed to be more relocatable (fewer mentions of the package name within files in the package). - The ``routesalchemy`` scaffold has been renamed ``alchemy``, replacing the older (traversal-based) ``alchemy`` scaffold (which has been retired). - The ``starter`` scaffold now uses URL dispatch by default. 1.2 (2011-09-12) ================ Features -------- - Route pattern replacement marker names can now begin with an underscore. See https://github.com/Pylons/pyramid/issues/276. 1.2b3 (2011-09-11) ================== Bug Fixes --------- - The route prefix was not taken into account when a static view was added in an "include". See https://github.com/Pylons/pyramid/issues/266 . 1.2b2 (2011-09-08) ================== Bug Fixes --------- - The 1.2b1 tarball was a brownbag (particularly for Windows users) because it contained filenames with stray quotation marks in inappropriate places. We depend on ``setuptools-git`` to produce release tarballs, and when it was run to produce the 1.2b1 tarball, it didn't yet cope well with files present in git repositories with high-order characters in their filenames. Documentation ------------- - Minor tweaks to the "Introduction" narrative chapter example app and wording. 1.2b1 (2011-09-08) ================== Bug Fixes --------- - Sometimes falling back from territory translations (``de_DE``) to language translations (``de``) would not work properly when using a localizer. See https://github.com/Pylons/pyramid/issues/263 - The static file serving machinery could not serve files that started with a ``.`` (dot) character. - Static files with high-order (super-ASCII) characters in their names could not be served by a static view. The static file serving machinery inappropriately URL-quoted path segments in filenames when asking for files from the filesystem. - Within ``pyramid.traversal.traversal_path`` , canonicalize URL segments from UTF-8 to Unicode before checking whether a segment matches literally one of ``.``, the empty string, or ``..`` in case there's some sneaky way someone might tunnel those strings via UTF-8 that don't match the literals before decoded. Documentation ------------- - Added a "What Makes Pyramid Unique" section to the Introduction narrative chapter. 1.2a6 (2011-09-06) ================== Bug Fixes --------- - AuthTktAuthenticationPolicy with a ``reissue_time`` interfered with logout. See https://github.com/Pylons/pyramid/issues/262. Internal -------- - Internalize code previously depended upon as imports from the ``paste.auth`` module (futureproof). - Replaced use of ``paste.urlparser.StaticURLParser`` with a derivative of Chris Rossi's "happy" static file serving code (futureproof). - Fixed test suite; on some systems tests would fail due to indeterminate test run ordering and a double-push-single-pop of a shared test variable. Behavior Differences -------------------- - An ETag header is no longer set when serving a static file. A Last-Modified header is set instead. - Static file serving no longer supports the ``wsgi.file_wrapper`` extension. - Instead of returning a ``403 Forbidden`` error when a static file is served that cannot be accessed by the Pyramid process' user due to file permissions, an IOError (or similar) will be raised. Scaffolds --------- - All scaffolds now send the ``cache_max_age`` parameter to the ``add_static_view`` method. 1.2a5 (2011-09-04) ================== Bug Fixes --------- - The ``route_prefix`` of a configurator was not properly taken into account when registering routes in certain circumstances. See https://github.com/Pylons/pyramid/issues/260 Dependencies ------------ - The ``zope.configuration`` package is no longer a dependency. 1.2a4 (2011-09-02) ================== Features -------- - Support an ``onerror`` keyword argument to ``pyramid.config.Configurator.scan()``. This onerror keyword argument is passed to ``venusian.Scanner.scan()`` to influence error behavior when an exception is raised during scanning. - The ``request_method`` predicate argument to ``pyramid.config.Configurator.add_view`` and ``pyramid.config.Configurator.add_route`` is now permitted to be a tuple of HTTP method names. Previously it was restricted to being a string representing a single HTTP method name. - Undeprecated ``pyramid.traversal.find_model``, ``pyramid.traversal.model_path``, ``pyramid.traversal.model_path_tuple``, and ``pyramid.url.model_url``, which were all deprecated in Pyramid 1.0. There's just not much cost to keeping them around forever as aliases to their renamed ``resource_*`` prefixed functions. - Undeprecated ``pyramid.view.bfg_view``, which was deprecated in Pyramid 1.0. This is a low-cost alias to ``pyramid.view.view_config`` which we'll just keep around forever. Dependencies ------------ - Pyramid now requires Venusian 1.0a1 or better to support the ``onerror`` keyword argument to ``pyramid.config.Configurator.scan``. 1.2a3 (2011-08-29) ================== Bug Fixes --------- - Pyramid did not properly generate static URLs using ``pyramid.url.static_url`` when passed a caller-package relative path due to a refactoring done in 1.2a1. - The ``settings`` object emitted a deprecation warning any time ``__getattr__`` was called upon it. However, there are legitimate situations in which ``__getattr__`` is called on arbitrary objects (e.g. ``hasattr``). Now, the ``settings`` object only emits the warning upon successful lookup. Internal -------- - Use ``config.with_package`` in view_config decorator rather than manufacturing a new renderer helper (cleanup). 1.2a2 (2011-08-27) ================== Bug Fixes --------- - When a ``renderers=`` argument is not specified to the Configurator constructor, eagerly register and commit the default renderer set. This permits the overriding of the default renderers, which was broken in 1.2a1 without a commit directly after Configurator construction. - Mako rendering exceptions had the wrong value for an error message. - An include could not set a root factory successfully because the Configurator constructor unconditionally registered one that would be treated as if it were "the word of the user". Features -------- - A session factory can now be passed in using the dotted name syntax. 1.2a1 (2011-08-24) ================== Features -------- - The ``[pshell]`` section in an ini configuration file now treats a ``setup`` key as a dotted name that points to a callable that is passed the bootstrap environment. It can mutate the environment as necessary for great justice. - A new configuration setting named ``pyramid.includes`` is now available. It is described in the "Environment Variables and ``.ini`` Files Settings" narrative documentation chapter. - Added a ``route_prefix`` argument to the ``pyramid.config.Configurator.include`` method. This argument allows you to compose URL dispatch applications together. See the section entitled "Using a Route Prefix to Compose Applications" in the "URL Dispatch" narrative documentation chapter. - Added a ``pyramid.security.NO_PERMISSION_REQUIRED`` constant for use in ``permission=`` statements to view configuration. This constant has a value of the string ``__no_permission_required__``. This string value was previously referred to in documentation; now the documentation uses the constant. - Added a decorator-based way to configure a response adapter: ``pyramid.response.response_adapter``. This decorator has the same use as ``pyramid.config.Configurator.add_response_adapter`` but it's declarative. - The ``pyramid.events.BeforeRender`` event now has an attribute named ``rendering_val``. This can be used to introspect the value returned by a view in a BeforeRender subscriber. - New configurator directive: ``pyramid.config.Configurator.add_tween``. This directive adds a "tween". A "tween" is used to wrap the Pyramid router's primary request handling function. This is a feature may be used by Pyramid framework extensions, to provide, for example, view timing support and as a convenient place to hang bookkeeping code. Tweens are further described in the narrative docs section in the Hooks chapter, named "Registering Tweens". - New paster command ``paster ptweens``, which prints the current "tween" configuration for an application. See the section entitled "Displaying Tweens" in the Command-Line Pyramid chapter of the narrative documentation for more info. - The Pyramid debug logger now uses the standard logging configuration (usually set up by Paste as part of startup). This means that output from e.g. ``debug_notfound``, ``debug_authorization``, etc. will go to the normal logging channels. The logger name of the debug logger will be the package name of the *caller* of the Configurator's constructor. - A new attribute is available on request objects: ``exc_info``. Its value will be ``None`` until an exception is caught by the Pyramid router, after which it will be the result of ``sys.exc_info()``. - ``pyramid.testing.DummyRequest`` now implements the ``add_finished_callback`` and ``add_response_callback`` methods. - New methods of the ``pyramid.config.Configurator`` class: ``set_authentication_policy`` and ``set_authorization_policy``. These are meant to be consumed mostly by add-on authors. - New Configurator method: ``set_root_factory``. - Pyramid no longer eagerly commits some default configuration statements at Configurator construction time, which permits values passed in as constructor arguments (e.g. ``authentication_policy`` and ``authorization_policy``) to override the same settings obtained via an "include". - Better Mako rendering exceptions via ``pyramid.mako_templating.MakoRenderingException`` - New request methods: ``current_route_url``, ``current_route_path``, and ``static_path``. - New functions in ``pyramid.url``: ``current_route_path`` and ``static_path``. - The ``pyramid.request.Request.static_url`` API (and its brethren ``pyramid.request.Request.static_path``, ``pyramid.url.static_url``, and ``pyramid.url.static_path``) now accept an asbolute filename as a "path" argument. This will generate a URL to an asset as long as the filename is in a directory which was previously registered as a static view. Previously, trying to generate a URL to an asset using an absolute file path would raise a ValueError. - The ``RemoteUserAuthenticationPolicy ``, ``AuthTktAuthenticationPolicy``, and ``SessionAuthenticationPolicy`` constructors now accept an additional keyword argument named ``debug``. By default, this keyword argument is ``False``. When it is ``True``, debug information will be sent to the Pyramid debug logger (usually on stderr) when the ``authenticated_userid`` or ``effective_principals`` method is called on any of these policies. The output produced can be useful when trying to diagnose authentication-related problems. - New view predicate: ``match_param``. Example: a view added via ``config.add_view(aview, match_param='action=edit')`` will be called only when the ``request.matchdict`` has a value inside it named ``action`` with a value of ``edit``. Internal -------- - The Pyramid "exception view" machinery is now implemented as a "tween" (``pyramid.tweens.excview_tween_factory``). - WSGIHTTPException (HTTPFound, HTTPNotFound, etc) now has a new API named "prepare" which renders the body and content type when it is provided with a WSGI environ. Required for debug toolbar. - Once ``__call__`` or ``prepare`` is called on a WSGIHTTPException, the body will be set, and subsequent calls to ``__call__`` will always return the same body. Delete the body attribute to rerender the exception body. - Previously the ``pyramid.events.BeforeRender`` event *wrapped* a dictionary (it addressed it as its ``_system`` attribute). Now it *is* a dictionary (it inherits from ``dict``), and it's the value that is passed to templates as a top-level dictionary. - The ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url`` functions in the ``pyramid.url`` package now delegate to a method on the request they've been passed, instead of the other way around. The pyramid.request.Request object now inherits from a mixin named pyramid.url.URLMethodsMixin to make this possible, and all url/path generation logic is embedded in this mixin. - Refactor ``pyramid.config`` into a package. - Removed the ``_set_security_policies`` method of the Configurator. - Moved the ``StaticURLInfo`` class from ``pyramid.static`` to ``pyramid.config.views``. - Move the ``Settings`` class from ``pyramid.settings`` to ``pyramid.config.settings``. - Move the ``OverrideProvider``, ``PackageOverrides``, ``DirectoryOverride``, and ``FileOverride`` classes from ``pyramid.asset`` to ``pyramid.config.assets``. Deprecations ------------ - All Pyramid-related deployment settings (e.g. ``debug_all``, ``debug_notfound``) are now meant to be prefixed with the prefix ``pyramid.``. For example: ``debug_all`` -> ``pyramid.debug_all``. The old non-prefixed settings will continue to work indefinitely but supplying them may eventually print a deprecation warning. All scaffolds and tutorials have been changed to use prefixed settings. - The ``settings`` dictionary now raises a deprecation warning when you attempt to access its values via ``__getattr__`` instead of via ``__getitem__``. Backwards Incompatibilities --------------------------- - If a string is passed as the ``debug_logger`` parameter to a Configurator, that string is considered to be the name of a global Python logger rather than a dotted name to an instance of a logger. - The ``pyramid.config.Configurator.include`` method now accepts only a single ``callable`` argument (a sequence of callables used to be permitted). If you are passing more than one ``callable`` to ``pyramid.config.Configurator.include``, it will break. You now must now instead make a separate call to the method for each callable. This change was introduced to support the ``route_prefix`` feature of include. - It may be necessary to more strictly order configuration route and view statements when using an "autocommitting" Configurator. In the past, it was possible to add a view which named a route name before adding a route with that name when you used an autocommitting configurator. For example:: config = Configurator(autocommit=True) config.add_view('my.pkg.someview', route_name='foo') config.add_route('foo', '/foo') The above will raise an exception when the view attempts to add itself. Now you must add the route before adding the view:: config = Configurator(autocommit=True) config.add_route('foo', '/foo') config.add_view('my.pkg.someview', route_name='foo') This won't effect "normal" users, only people who have legacy BFG codebases that used an autommitting configurator and possibly tests that use the configurator API (the configurator returned by ``pyramid.testing.setUp`` is an autocommitting configurator). The right way to get around this is to use a non-autocommitting configurator (the default), which does not have these directive ordering requirements. - The ``pyramid.config.Configurator.add_route`` directive no longer returns a route object. This change was required to make route vs. view configuration processing work properly. Documentation ------------- - Narrative and API documentation which used the ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url`` functions in the ``pyramid.url`` package have now been changed to use eponymous methods of the request instead. - Added a section entitled "Using a Route Prefix to Compose Applications" to the "URL Dispatch" narrative documentation chapter. - Added a new module to the API docs: ``pyramid.tweens``. - Added a "Registering Tweens" section to the "Hooks" narrative chapter. - Added a "Displaying Tweens" section to the "Command-Line Pyramid" narrative chapter. - Added documentation for the ``pyramid.tweens`` and ``pyramid.includes`` configuration settings to the "Environment Variables and ``.ini`` Files Settings" chapter. - Added a Logging chapter to the narrative docs (based on the Pylons logging docs, thanks Phil). - Added a Paste chapter to the narrative docs (moved content from the Project chapter). - Added the ``pyramid.interfaces.IDict`` interface representing the methods of a dictionary, for documentation purposes only (IMultiDict and IBeforeRender inherit from it). - All tutorials now use - The ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url`` methods of the request rather than the function variants imported from ``pyramid.url``. - The ZODB wiki tutorial now uses the ``pyramid_zodbconn`` package rather than the ``repoze.zodbconn`` package to provide ZODB integration. Dependency Changes ------------------ - Pyramid now relies on PasteScript >= 1.7.4. This version contains a feature important for allowing flexible logging configuration. Scaffolds ---------- - All scaffolds now use the ``pyramid_tm`` package rather than the ``repoze.tm2`` middleware to manage transaction management. - The ZODB scaffold now uses the ``pyramid_zodbconn`` package rather than the ``repoze.zodbconn`` package to provide ZODB integration. - All scaffolds now use the ``pyramid_debugtoolbar`` package rather than the ``WebError`` package to provide interactive debugging features. - Projects created via a scaffold no longer depend on the ``WebError`` package at all; configuration in the ``production.ini`` file which used to require its ``error_catcher`` middleware has been removed. Configuring error catching / email sending is now the domain of the ``pyramid_exclog`` package (see http://docs.pylonsproject.org/projects/pyramid_exclog/dev/). Bug Fixes --------- - Fixed an issue with the default renderer not working at certain times. See https://github.com/Pylons/pyramid/issues/249 1.1 (2011-07-22) ================ Features -------- - Added the ``pyramid.renderers.null_renderer`` object as an API. The null renderer is an object that can be used in advanced integration cases as input to the view configuration ``renderer=`` argument. When the null renderer is used as a view renderer argument, Pyramid avoids converting the view callable result into a Response object. This is useful if you want to reuse the view configuration and lookup machinery outside the context of its use by the Pyramid router. This feature was added for consumption by the ``pyramid_rpc`` package, which uses view configuration and lookup outside the context of a router in exactly this way. ``pyramid_rpc`` has been broken under 1.1 since 1.1b1; adding it allows us to make it work again. - Change all scaffolding templates that point to docs.pylonsproject.org to use ``/projects/pyramid/current`` rather than ``/projects/pyramid/dev``. Internals --------- - Remove ``compat`` code that served only the purpose of providing backwards compatibility with Python 2.4. - Add a deprecation warning for non-API function ``pyramid.renderers.renderer_from_name`` which has seen use in the wild. - Add a ``clone`` method to ``pyramid.renderers.RendererHelper`` for use by the ``pyramid.view.view_config`` decorator. Documentation ------------- - Fixed two typos in wiki2 (SQLA + URL Dispatch) tutorial. - Reordered chapters in narrative section for better new user friendliness. - Added more indexing markers to sections in documentation. 1.1b4 (2011-07-18) ================== Documentation ------------- - Added a section entitled "Writing a Script" to the "Command-Line Pyramid" chapter. Backwards Incompatibilities --------------------------- - We added the ``pyramid.scripting.make_request`` API too hastily in 1.1b3. It has been removed. Sorry for any inconvenience. Use the ``pyramid.request.Request.blank`` API instead. Features -------- - The ``paster pshell``, ``paster pviews``, and ``paster proutes`` commands each now under the hood uses ``pyramid.paster.bootstrap``, which makes it possible to supply an ``.ini`` file without naming the "right" section in the file that points at the actual Pyramid application. Instead, you can generally just run ``paster {pshell|proutes|pviews} development.ini`` and it will do mostly the right thing. Bug Fixes --------- - Omit custom environ variables when rendering a custom exception template in ``pyramid.httpexceptions.WSGIHTTPException._set_default_attrs``; stringifying thse may trigger code that should not be executed; see https://github.com/Pylons/pyramid/issues/239 1.1b3 (2011-07-15) ================== Features -------- - Fix corner case to ease semifunctional testing of views: create a new rendererinfo to clear out old registry on a rescan. See https://github.com/Pylons/pyramid/pull/234. - New API class: ``pyramid.static.static_view``. This supersedes the deprecated ``pyramid.view.static`` class. ``pyramid.static.static_view`` by default serves up documents as the result of the request's ``path_info``, attribute rather than it's ``subpath`` attribute (the inverse was true of ``pyramid.view.static``, and still is). ``pyramid.static.static_view`` exposes a ``use_subpath`` flag for use when you want the static view to behave like the older deprecated version. - A new API function ``pyramid.paster.bootstrap`` has been added to make writing scripts that bootstrap a Pyramid environment easier, e.g.:: from pyramid.paster import bootstrap info = bootstrap('/path/to/my/development.ini') request = info['request'] print request.route_url('myroute') - A new API function ``pyramid.scripting.prepare`` has been added. It is a lower-level analogue of ``pyramid.paster.boostrap`` that accepts a request and a registry instead of a config file argument, and is used for the same purpose:: from pyramid.scripting import prepare info = prepare(registry=myregistry) request = info['request'] print request.route_url('myroute') - A new API function ``pyramid.scripting.make_request`` has been added. The resulting request will have a ``registry`` attribute. It is meant to be used in conjunction with ``pyramid.scripting.prepare`` and/or ``pyramid.paster.bootstrap`` (both of which accept a request as an argument):: from pyramid.scripting import make_request request = make_request('/') - New API attribute ``pyramid.config.global_registries`` is an iterable object that contains references to every Pyramid registry loaded into the current process via ``pyramid.config.Configurator.make_app``. It also has a ``last`` attribute containing the last registry loaded. This is used by the scripting machinery, and is available for introspection. Deprecations ------------ - The ``pyramid.view.static`` class has been deprecated in favor of the newer ``pyramid.static.static_view`` class. A deprecation warning is raised when it is used. You should replace it with a reference to ``pyramid.static.static_view`` with the ``use_subpath=True`` argument. Bug Fixes --------- - Without a mo-file loaded for the combination of domain/locale, ``pyramid.i18n.Localizer.pluralize`` run using that domain/locale combination raised an inscrutable "translations object has no attr 'plural'" error. Now, instead it "works" (it uses a germanic pluralization by default). It's nonsensical to try to pluralize something without translations for that locale/domain available, but this behavior matches the behavior of ``pyramid.i18n.Localizer.translate`` so it's at least consistent; see https://github.com/Pylons/pyramid/issues/235. 1.1b2 (2011-07-13) ================== Features -------- - New environment setting ``PYRAMID_PREVENT_HTTP_CACHE`` and new configuration file value ``prevent_http_cache``. These are synomymous and allow you to prevent HTTP cache headers from being set by Pyramid's ``http_cache`` machinery globally in a process. see the "Influencing HTTP Caching" section of the "View Configuration" narrative chapter and the detailed documentation for this setting in the "Environment Variables and Configuration Settings" narrative chapter. Behavior Changes ---------------- - Previously, If a ``BeforeRender`` event subscriber added a value via the ``__setitem__`` or ``update`` methods of the event object with a key that already existed in the renderer globals dictionary, a ``KeyError`` was raised. With the deprecation of the "add_renderer_globals" feature of the configurator, there was no way to override an existing value in the renderer globals dictionary that already existed. Now, the event object will overwrite an older value that is already in the globals dictionary when its ``__setitem__`` or ``update`` is called (as well as the new ``setdefault`` method), just like a plain old dictionary. As a result, for maximum interoperability with other third-party subscribers, if you write an event subscriber meant to be used as a BeforeRender subscriber, your subscriber code will now need to (using ``.get`` or ``__contains__`` of the event object) ensure no value already exists in the renderer globals dictionary before setting an overriding value. Bug Fixes --------- - The ``Configurator.add_route`` method allowed two routes with the same route to be added without an intermediate ``config.commit()``. If you now receive a ``ConfigurationError`` at startup time that appears to be ``add_route`` related, you'll need to either a) ensure that all of your route names are unique or b) call ``config.commit()`` before adding a second route with the name of a previously added name or c) use a Configurator that works in ``autocommit`` mode. - The ``pyramid_routesalchemy`` and ``pyramid_alchemy`` scaffolds inappropriately used ``DBSession.rollback()`` instead of ``transaction.abort()`` in one place. - We now clear ``request.response`` before we invoke an exception view; an exception view will be working with a request.response that has not been touched by any code prior to the exception. - Views associated with routes with spaces in the route name may not have been looked up correctly when using Pyramid with ``zope.interface`` 3.6.4 and better. See https://github.com/Pylons/pyramid/issues/232. Documentation ------------- - Wiki2 (SQLAlchemy + URL Dispatch) tutorial ``models.initialize_sql`` didn't match the ``pyramid_routesalchemy`` scaffold function of the same name; it didn't get synchronized when it was changed in the scaffold. - New documentation section in View Configuration narrative chapter: "Influencing HTTP Caching". 1.1b1 (2011-07-10) ================== Features -------- - It is now possible to invoke ``paster pshell`` even if the paste ini file section name pointed to in its argument is not actually a Pyramid WSGI application. The shell will work in a degraded mode, and will warn the user. See "The Interactive Shell" in the "Creating a Pyramid Project" narrative documentation section. - ``paster pshell`` now offers more built-in global variables by default (including ``app`` and ``settings``). See "The Interactive Shell" in the "Creating a Pyramid Project" narrative documentation section. - It is now possible to add a ``[pshell]`` section to your application's .ini configuration file, which influences the global names available to a pshell session. See "Extending the Shell" in the "Creating a Pyramid Project" narrative documentation chapter. - The ``config.scan`` method has grown a ``**kw`` argument. ``kw`` argument represents a set of keyword arguments to pass to the Venusian ``Scanner`` object created by Pyramid. (See the Venusian documentation for more information about ``Scanner``). - New request property: ``json_body``. This property will return the JSON-decoded variant of the request body. If the request body is not well-formed JSON, this property will raise an exception. - A new value ``http_cache`` can be used as a view configuration parameter. 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})``. Bug Fixes --------- - Framework wrappers of the original view (such as http_cached and so on) relied on being able to trust that the response they were receiving was an IResponse. It wasn't always, because the response was resolved by the router instead of early in the view wrapping process. This has been fixed. Documentation ------------- - Added a section in the "Webob" chapter named "Dealing With A JSON-Encoded Request Body" (usage of ``request.json_body``). Behavior Changes ---------------- - The ``paster pshell``, ``paster proutes``, and ``paster pviews`` commands now take a single argument in the form ``/path/to/config.ini#sectionname`` rather than the previous 2-argument spelling ``/path/to/config.ini sectionname``. ``#sectionname`` may be omitted, in which case ``#main`` is assumed. 1.1a4 (2011-07-01) ================== Bug Fixes --------- - ``pyramid.testing.DummyRequest`` now raises deprecation warnings when attributes deprecated for ``pyramid.request.Request`` are accessed (like ``response_content_type``). This is for the benefit of folks running unit tests which use DummyRequest instead of a "real" request, so they know things are deprecated without necessarily needing a functional test suite. - The ``pyramid.events.subscriber`` directive behaved contrary to the documentation when passed more than one interface object to its constructor. For example, when the following listener was registered:: @subscriber(IFoo, IBar) def expects_ifoo_events_and_ibar_events(event): print event The Events chapter docs claimed that the listener would be registered and listening for both ``IFoo`` and ``IBar`` events. Instead, it registered an "object event" subscriber which would only be called if an IObjectEvent was emitted where the object interface was ``IFoo`` and the event interface was ``IBar``. The behavior now matches the documentation. If you were relying on the buggy behavior of the 1.0 ``subscriber`` directive in order to register an object event subscriber, you must now pass a sequence to indicate you'd like to register a subscriber for an object event. e.g.:: @subscriber([IFoo, IBar]) def expects_object_event(object, event): print object, event Features -------- - Add JSONP renderer (see "JSONP renderer" in the Renderers chapter of the documentation). Deprecations ------------ - Deprecated the ``set_renderer_globals_factory`` method of the Configurator and the ``renderer_globals`` Configurator constructor parameter. Documentation ------------- - The Wiki and Wiki2 tutorial "Tests" chapters each had two bugs: neither did told the user to depend on WebTest, and 2 tests failed in each as the result of changes to Pyramid itself. These issues have been fixed. - Move 1.0.X CHANGES.txt entries to HISTORY.txt. 1.1a3 (2011-06-26) ================== Features -------- - Added ``mako.preprocessor`` config file parameter; allows for a Mako preprocessor to be specified as a Python callable or Python dotted name. See https://github.com/Pylons/pyramid/pull/183 for rationale. Bug fixes --------- - Pyramid would raise an AttributeError in the Configurator when attempting to set a ``__text__`` attribute on a custom predicate that was actually a classmethod. See https://github.com/Pylons/pyramid/pull/217 . - Accessing or setting deprecated response_* attrs on request (e.g. ``response_content_type``) now issues a deprecation warning at access time rather than at rendering time. 1.1a2 (2011-06-22) ================== Bug Fixes --------- - 1.1a1 broke Akhet by not providing a backwards compatibility import shim for ``pyramid.paster.PyramidTemplate``. Now one has been added, although a deprecation warning is emitted when Akhet imports it. - If multiple specs were provided in a single call to ``config.add_translation_dirs``, the directories were inserted into the beginning of the directory list in the wrong order: they were inserted in the reverse of the order they were provided in the ``*specs`` list (items later in the list were added before ones earlier in the list). This is now fixed. Backwards Incompatibilities --------------------------- - The pyramid Router attempted to set a value into the key ``environ['repoze.bfg.message']`` when it caught a view-related exception for backwards compatibility with applications written for ``repoze.bfg`` during error handling. It did this by using code that looked like so:: # "why" is an exception object try: msg = why[0] except: msg = '' environ['repoze.bfg.message'] = msg Use of the value ``environ['repoze.bfg.message']`` was docs-deprecated in Pyramid 1.0. Our standing policy is to not remove features after a deprecation for two full major releases, so this code was originally slated to be removed in Pyramid 1.2. However, computing the ``repoze.bfg.message`` value was the source of at least one bug found in the wild (https://github.com/Pylons/pyramid/issues/199), and there isn't a foolproof way to both preserve backwards compatibility and to fix the bug. Therefore, the code which sets the value has been removed in this release. Code in exception views which relies on this value's presence in the environment should now use the ``exception`` attribute of the request (e.g. ``request.exception[0]``) to retrieve the message instead of relying on ``request.environ['repoze.bfg.message']``. 1.1a1 (2011-06-20) ================== Documentation ------------- - The term "template" used to refer to both "paster templates" and "rendered templates" (templates created by a rendering engine. i.e. Mako, Chameleon, Jinja, etc.). "Paster templates" will now be refered to as "scaffolds", whereas the name for "rendered templates" will remain as "templates." - The ``wiki`` (ZODB+Traversal) tutorial was updated slightly. - The ``wiki2`` (SQLA+URL Dispatch) tutorial was updated slightly. - Make ``pyramid.interfaces.IAuthenticationPolicy`` and ``pyramid.interfaces.IAuthorizationPolicy`` public interfaces, and refer to them within the ``pyramid.authentication`` and ``pyramid.authorization`` API docs. - Render the function definitions for each exposed interface in ``pyramid.interfaces``. - Add missing docs reference to ``pyramid.config.Configurator.set_view_mapper`` and refer to it within Hooks chapter section named "Using a View Mapper". - Added section to the "Environment Variables and ``.ini`` File Settings" chapter in the narrative documentation section entitled "Adding a Custom Setting". - Added documentation for a "multidict" (e.g. the API of ``request.POST``) as interface API documentation. - Added a section to the "URL Dispatch" narrative chapter regarding the new "static" route feature. - Added "What's New in Pyramid 1.1" to HTML rendering of documentation. - Added API docs for ``pyramid.authentication.SessionAuthenticationPolicy``. - Added API docs for ``pyramid.httpexceptions.exception_response``. - Added "HTTP Exceptions" section to Views narrative chapter including a description of ``pyramid.httpexceptions.exception_response``. Features -------- - Add support for language fallbacks: when trying to translate for a specific territory (such as ``en_GB``) fall back to translations for the language (ie ``en``). This brings the translation behaviour in line with GNU gettext and fixes partially translated texts when using C extensions. - New authentication policy: ``pyramid.authentication.SessionAuthenticationPolicy``, which uses a session to store credentials. - Accessing the ``response`` attribute of a ``pyramid.request.Request`` object (e.g. ``request.response`` within a view) now produces a new ``pyramid.response.Response`` object. This feature is meant to be used mainly when a view configured with a renderer needs to set response attributes: all renderers will use the Response object implied by ``request.response`` as the response object returned to the router. ``request.response`` can also be used by code in a view that does not use a renderer, however the response object that is produced by ``request.response`` must be returned when a renderer is not in play (it is not a "global" response). - Integers and longs passed as ``elements`` to ``pyramid.url.resource_url`` or ``pyramid.request.Request.resource_url`` e.g. ``resource_url(context, request, 1, 2)`` (``1`` and ``2`` are the ``elements``) will now be converted implicitly to strings in the result. Previously passing integers or longs as elements would cause a TypeError. - ``pyramid_alchemy`` paster template now uses ``query.get`` rather than ``query.filter_by`` to take better advantage of identity map caching. - ``pyramid_alchemy`` paster template now has unit tests. - Added ``pyramid.i18n.make_localizer`` API (broken out from ``get_localizer`` guts). - An exception raised by a NewRequest event subscriber can now be caught by an exception view. - It is now possible to get information about why Pyramid raised a Forbidden exception from within an exception view. The ``ACLDenied`` object returned by the ``permits`` method of each stock authorization policy (``pyramid.interfaces.IAuthorizationPolicy.permits``) is now attached to the Forbidden exception as its ``result`` attribute. Therefore, if you've created a Forbidden exception view, you can see the ACE, ACL, permission, and principals involved in the request as eg. ``context.result.permission``, ``context.result.acl``, etc within the logic of the Forbidden exception view. - Don't explicitly prevent the ``timeout`` from being lower than the ``reissue_time`` when setting up an ``AuthTktAuthenticationPolicy`` (previously such a configuration would raise a ``ValueError``, now it's allowed, although typically nonsensical). Allowing the nonsensical configuration made the code more understandable and required fewer tests. - A new paster command named ``paster pviews`` was added. This command prints a summary of potentially matching views for a given path. See the section entitled "Displaying Matching Views for a Given URL" in the "View Configuration" chapter of the narrative documentation for more information. - The ``add_route`` method of the Configurator now accepts a ``static`` argument. If this argument is ``True``, the added route will never be considered for matching when a request is handled. Instead, it will only be useful for URL generation via ``route_url`` and ``route_path``. See the section entitled "Static Routes" in the URL Dispatch narrative chapter for more information. - A default exception view for the context ``pyramid.interfaces.IExceptionResponse`` is now registered by default. This means that an instance of any exception response class imported from ``pyramid.httpexceptions`` (such as ``HTTPFound``) can now be raised from within view code; when raised, this exception view will render the exception to a response. - A function named ``pyramid.httpexceptions.exception_response`` is a shortcut that can be used to create HTTP exception response objects using an HTTP integer status code. - The Configurator now accepts an additional keyword argument named ``exceptionresponse_view``. By default, this argument is populated with a default exception view function that will be used when a response is raised as an exception. When ``None`` is passed for this value, an exception view for responses will not be registered. Passing ``None`` returns the behavior of raising an HTTP exception to that of Pyramid 1.0 (the exception will propagate to middleware and to the WSGI server). - The ``pyramid.request.Request`` class now has a ``ResponseClass`` interface which points at ``pyramid.response.Response``. - The ``pyramid.response.Response`` class now has a ``RequestClass`` interface which points at ``pyramid.request.Request``. - It is now possible to return an arbitrary object from a Pyramid view callable even if a renderer is not used, as long as a suitable adapter to ``pyramid.interfaces.IResponse`` is registered for the type of the returned object by using the new ``pyramid.config.Configurator.add_response_adapter`` API. See the section in the Hooks chapter of the documentation entitled "Changing How Pyramid Treats View Responses". - The Pyramid router will now, by default, call the ``__call__`` method of WebOb response objects when returning a WSGI response. This means that, among other things, the ``conditional_response`` feature of WebOb response objects will now behave properly. - New method named ``pyramid.request.Request.is_response``. This method should be used instead of the ``pyramid.view.is_response`` function, which has been deprecated. Bug Fixes --------- - URL pattern markers used in URL dispatch are permitted to specify a custom regex. For example, the pattern ``/{foo:\d+}`` means to match ``/12345`` (foo==12345 in the match dictionary) but not ``/abc``. However, custom regexes in a pattern marker which used squiggly brackets did not work. For example, ``/{foo:\d{4}}`` would fail to match ``/1234`` and ``/{foo:\d{1,2}}`` would fail to match ``/1`` or ``/11``. One level of inner squiggly brackets is now recognized so that the prior two patterns given as examples now work. See also https://github.com/Pylons/pyramid/issues/#issue/123. - Don't send port numbers along with domain information in cookies set by AuthTktCookieHelper (see https://github.com/Pylons/pyramid/issues/131). - ``pyramid.url.route_path`` (and the shortcut ``pyramid.request.Request.route_url`` method) now include the WSGI SCRIPT_NAME at the front of the path if it is not empty (see https://github.com/Pylons/pyramid/issues/135). - ``pyramid.testing.DummyRequest`` now has a ``script_name`` attribute (the empty string). - Don't quote ``:@&+$,`` symbols in ``*elements`` passed to ``pyramid.url.route_url`` or ``pyramid.url.resource_url`` (see https://github.com/Pylons/pyramid/issues#issue/141). - Include SCRIPT_NAME in redirects issued by ``pyramid.view.append_slash_notfound_view`` (see https://github.com/Pylons/pyramid/issues#issue/149). - Static views registered with ``config.add_static_view`` which also included a ``permission`` keyword argument would not work as expected, because ``add_static_view`` also registered a route factory internally. Because a route factory was registered internally, the context checked by the Pyramid permission machinery never had an ACL. ``add_static_view`` no longer registers a route with a factory, so the default root factory will be used. - ``config.add_static_view`` now passes extra keyword arguments it receives to ``config.add_route`` (calling add_static_view is mostly logically equivalent to adding a view of the type ``pyramid.static.static_view`` hooked up to a route with a subpath). This makes it possible to pass e.g., ``factory=`` to ``add_static_view`` to protect a particular static view with a custom ACL. - ``testing.DummyRequest`` used the wrong registry (the global registry) as ``self.registry`` if a dummy request was created *before* ``testing.setUp`` was executed (``testing.setUp`` pushes a local registry onto the threadlocal stack). Fixed by implementing ``registry`` as a property for DummyRequest instead of eagerly assigning an attribute. See also https://github.com/Pylons/pyramid/issues/165 - When visiting a URL that represented a static view which resolved to a subdirectory, the ``index.html`` of that subdirectory would not be served properly. Instead, a redirect to ``/subdir`` would be issued. This has been fixed, and now visiting a subdirectory that contains an ``index.html`` within a static view returns the index.html properly. See also https://github.com/Pylons/pyramid/issues/67. - Redirects issued by a static view did not take into account any existing ``SCRIPT_NAME`` (such as one set by a url mapping composite). Now they do. - The ``pyramid.wsgi.wsgiapp2`` decorator did not take into account the ``SCRIPT_NAME`` in the origin request. - The ``pyramid.wsgi.wsgiapp2`` decorator effectively only worked when it decorated a view found via traversal; it ignored the ``PATH_INFO`` that was part of a url-dispatch-matched view. Deprecations ------------ - Deprecated all assignments to ``request.response_*`` attributes (for example ``request.response_content_type = 'foo'`` is now deprecated). Assignments and mutations of assignable request attributes that were considered by the framework for response influence are now deprecated: ``response_content_type``, ``response_headerlist``, ``response_status``, ``response_charset``, and ``response_cache_for``. Instead of assigning these to the request object for later detection by the rendering machinery, users should use the appropriate API of the Response object created by accessing ``request.response`` (e.g. code which does ``request.response_content_type = 'abc'`` should be changed to ``request.response.content_type = 'abc'``). - Passing view-related parameters to ``pyramid.config.Configurator.add_route`` is now deprecated. Previously, a view was permitted to be connected to a route using a set of ``view*`` parameters passed to the ``add_route`` method of the Configurator. This was a shorthand which replaced the need to perform a subsequent call to ``add_view``. For example, it was valid (and often recommended) to do:: config.add_route('home', '/', view='mypackage.views.myview', view_renderer='some/renderer.pt') Passing ``view*`` arguments to ``add_route`` is now deprecated in favor of connecting a view to a predefined route via ``Configurator.add_view`` using the route's ``route_name`` parameter. As a result, the above example should now be spelled:: config.add_route('home', '/') config.add_view('mypackage.views.myview', route_name='home') renderer='some/renderer.pt') This deprecation was done to reduce confusion observed in IRC, as well as to (eventually) reduce documentation burden (see also https://github.com/Pylons/pyramid/issues/164). A deprecation warning is now issued when any view-related parameter is passed to ``Configurator.add_route``. - Passing an ``environ`` dictionary to the ``__call__`` method of a "traverser" (e.g. an object that implements ``pyramid.interfaces.ITraverser`` such as an instance of ``pyramid.traversal.ResourceTreeTraverser``) as its ``request`` argument now causes a deprecation warning to be emitted. Consumer code should pass a ``request`` object instead. The fact that passing an environ dict is permitted has been documentation-deprecated since ``repoze.bfg`` 1.1, and this capability will be removed entirely in a future version. - The following (undocumented, dictionary-like) methods of the ``pyramid.request.Request`` object have been deprecated: ``__contains__``, ``__delitem__``, ``__getitem__``, ``__iter__``, ``__setitem__``, ``get``, ``has_key``, ``items``, ``iteritems``, ``itervalues``, ``keys``, ``pop``, ``popitem``, ``setdefault``, ``update``, and ``values``. Usage of any of these methods will cause a deprecation warning to be emitted. These methods were added for internal compatibility in ``repoze.bfg`` 1.1 (code that currently expects a request object expected an environ object in BFG 1.0 and before). In a future version, these methods will be removed entirely. - Deprecated ``pyramid.view.is_response`` function in favor of (newly-added) ``pyramid.request.Request.is_response`` method. Determining if an object is truly a valid response object now requires access to the registry, which is only easily available as a request attribute. The ``pyramid.view.is_response`` function will still work until it is removed, but now may return an incorrect answer under some (very uncommon) circumstances. Behavior Changes ---------------- - The default Mako renderer is now configured to escape all HTML in expression tags. This is intended to help prevent XSS attacks caused by rendering unsanitized input from users. To revert this behavior in user's templates, they need to filter the expression through the 'n' filter. For example, ${ myhtml | n }. See https://github.com/Pylons/pyramid/issues/193. - A custom request factory is now required to return a request object that has a ``response`` attribute (or "reified"/lazy property) if they the request is meant to be used in a view that uses a renderer. This ``response`` attribute should be an instance of the class ``pyramid.response.Response``. - The JSON and string renderer factories now assign to ``request.response.content_type`` rather than ``request.response_content_type``. - Each built-in renderer factory now determines whether it should change the content type of the response by comparing the response's content type against the response's default content type; if the content type is the default content type (usually ``text/html``), the renderer changes the content type (to ``application/json`` or ``text/plain`` for JSON and string renderers respectively). - The ``pyramid.wsgi.wsgiapp2`` now uses a slightly different method of figuring out how to "fix" ``SCRIPT_NAME`` and ``PATH_INFO`` for the downstream application. As a result, those values may differ slightly from the perspective of the downstream application (for example, ``SCRIPT_NAME`` will now never possess a trailing slash). - Previously, ``pyramid.request.Request`` inherited from ``webob.request.Request`` and implemented ``__getattr__``, ``__setattr__`` and ``__delattr__`` itself in order to overidde "adhoc attr" WebOb behavior where attributes of the request are stored in the environ. Now, ``pyramid.request.Request`` object inherits from (the more recent) ``webob.request.BaseRequest`` instead of ``webob.request.Request``, which provides the same behavior. ``pyramid.request.Request`` no longer implements its own ``__getattr__``, ``__setattr__`` or ``__delattr__`` as a result. - ``pyramid.response.Response`` is now a *subclass* of ``webob.response.Response`` (in order to directly implement the ``pyramid.interfaces.IResponse`` interface). - The "exception response" objects importable from ``pyramid.httpexceptions`` (e.g. ``HTTPNotFound``) are no longer just import aliases for classes that actually live in ``webob.exc``. Instead, we've defined our own exception classes within the module that mirror and emulate the ``webob.exc`` exception response objects almost entirely. See the "Design Defense" doc section named "Pyramid Uses its Own HTTP Exception Classes" for more information. Backwards Incompatibilities --------------------------- - Pyramid no longer supports Python 2.4. Python 2.5 or better is required to run Pyramid 1.1+. - The Pyramid router now, by default, expects response objects returned from view callables to implement the ``pyramid.interfaces.IResponse`` interface. Unlike the Pyramid 1.0 version of this interface, objects which implement IResponse now must define a ``__call__`` method that accepts ``environ`` and ``start_response``, and which returns an ``app_iter`` iterable, among other things. Previously, it was possible to return any object which had the three WebOb ``app_iter``, ``headerlist``, and ``status`` attributes as a response, so this is a backwards incompatibility. It is possible to get backwards compatibility back by registering an adapter to IResponse from the type of object you're now returning from view callables. See the section in the Hooks chapter of the documentation entitled "Changing How Pyramid Treats View Responses". - The ``pyramid.interfaces.IResponse`` interface is now much more extensive. Previously it defined only ``app_iter``, ``status`` and ``headerlist``; now it is basically intended to directly mirror the ``webob.Response`` API, which has many methods and attributes. - The ``pyramid.httpexceptions`` classes named ``HTTPFound``, ``HTTPMultipleChoices``, ``HTTPMovedPermanently``, ``HTTPSeeOther``, ``HTTPUseProxy``, and ``HTTPTemporaryRedirect`` now accept ``location`` as their first positional argument rather than ``detail``. This means that you can do, e.g. ``return pyramid.httpexceptions.HTTPFound('http://foo')`` rather than ``return pyramid.httpexceptions.HTTPFound(location='http//foo')`` (the latter will of course continue to work). Dependencies ------------ - Pyramid now depends on WebOb >= 1.0.2 as tests depend on the bugfix in that release: "Fix handling of WSGI environs with missing ``SCRIPT_NAME``". (Note that in reality, everyone should probably be using 1.0.4 or better though, as WebOb 1.0.2 and 1.0.3 were effectively brownbag releases.) 1.0 (2011-01-30) ================ Documentation ------------- - Fixed bug in ZODB Wiki tutorial (missing dependency on ``docutils`` in "models" step within ``setup.py``). - Removed API documentation for ``pyramid.testing`` APIs named ``registerDummySecurityPolicy``, ``registerResources``, ``registerModels``, ``registerEventListener``, ``registerTemplateRenderer``, ``registerDummyRenderer``, ``registerView``, ``registerUtility``, ``registerAdapter``, ``registerSubscriber``, ``registerRoute``, and ``registerSettings``. - Moved "Using ZODB With ZEO" and "Using repoze.catalog Within Pyramid" tutorials out of core documentation and into the Pyramid Tutorials site (http://docs.pylonsproject.org/projects/pyramid_tutorials/dev/). - Changed "Cleaning up After a Request" section in the URL Dispatch chapter to use ``request.add_finished_callback`` instead of jamming an object with a ``__del__`` into the WSGI environment. - Remove duplication of ``add_route`` API documentation from URL Dispatch narrative chapter. - Remove duplication of API and narrative documentation in ``pyramid.view.view_config`` API docs by pointing to ``pyramid.config.add_view`` documentation and narrative chapter documentation. - Removed some API documentation duplicated in narrative portions of documentation - Removed "Overall Flow of Authentication" from SQLAlchemy + URL Dispatch wiki tutorial due to print space concerns (moved to Pyramid Tutorials site). Bug Fixes --------- - Deprecated-since-BFG-1.2 APIs from ``pyramid.testing`` now properly emit deprecation warnings. - Added ``egg:repoze.retry#retry`` middleware to the WSGI pipeline in ZODB templates (retry ZODB conflict errors which occur in normal operations). - Removed duplicate implementations of ``is_response``. Two competing implementations existed: one in ``pyramid.config`` and one in ``pyramid.view``. Now the one defined in ``pyramid.view`` is used internally by ``pyramid.config`` and continues to be advertised as an API. 1.0b3 (2011-01-28) ================== Bug Fixes --------- - Use © instead of copyright symbol in paster templates / tutorial templates for the benefit of folks who cutnpaste and save to a non-UTF8 format. - ``pyramid.view.append_slash_notfound_view`` now preserves GET query parameters across redirects. Documentation ------------- - Beef up documentation related to ``set_default_permission``: explicitly mention that default permissions also protect exception views. - Paster templates and tutorials now use spaces instead of tabs in their HTML templates. 1.0b2 (2011-01-24) ================== Bug Fixes --------- - The ``production.ini`` generated by all paster templates now have an effective logging level of WARN, which prevents e.g. SQLAlchemy statement logging and other inappropriate output. - The ``production.ini`` of the ``pyramid_routesalchemy`` and ``pyramid_alchemy`` paster templates did not have a ``sqlalchemy`` logger section, preventing ``paster serve production.ini`` from working. - The ``pyramid_routesalchemy`` and ``pyramid_alchemy`` paster templates used the ``{{package}}`` variable in a place where it should have used the ``{{project}}`` variable, causing applications created with uppercase letters e.g. ``paster create -t pyramid_routesalchemy Dibbus`` to fail to start when ``paster serve development.ini`` was used against the result. See https://github.com/Pylons/pyramid/issues/#issue/107 - The ``render_view`` method of ``pyramid.renderers.RendererHelper`` passed an incorrect value into the renderer for ``renderer_info``. It now passes an instance of ``RendererHelper`` instead of a dictionary, which is consistent with other usages. See https://github.com/Pylons/pyramid/issues#issue/106 - A bug existed in the ``pyramid.authentication.AuthTktCookieHelper`` which would break any usage of an AuthTktAuthenticationPolicy when one was configured to reissue its tokens (``reissue_time`` < ``timeout`` / ``max_age``). Symptom: ``ValueError: ('Invalid token %r', '')``. See https://github.com/Pylons/pyramid/issues#issue/108. 1.0b1 (2011-01-21) ================== Features -------- - The AuthTktAuthenticationPolicy now accepts a ``tokens`` parameter via ``pyramid.security.remember``. The value must be a sequence of strings. Tokens are placed into the auth_tkt "tokens" field and returned in the auth_tkt cookie. - Add ``wild_domain`` argument to AuthTktAuthenticationPolicy, which defaults to ``True``. If it is set to ``False``, the feature of the policy which sets a cookie with a wilcard domain will be turned off. - Add a ``MANIFEST.in`` file to each paster template. See https://github.com/Pylons/pyramid/issues#issue/95 Bug Fixes --------- - ``testing.setUp`` now adds a ``settings`` attribute to the registry (both when it's passed a registry without any settings and when it creates one). - The ``testing.setUp`` function now takes a ``settings`` argument, which should be a dictionary. Its values will subsequently be available on the returned ``config`` object as ``config.registry.settings``. Documentation ------------- - Added "What's New in Pyramid 1.0" chapter to HTML rendering of documentation. - Merged caseman-master narrative editing branch, many wording fixes and extensions. - Fix deprecated example showing ``chameleon_zpt`` API call in testing narrative chapter. - Added "Adding Methods to the Configurator via ``add_directive``" section to Advanced Configuration narrative chapter. - Add docs for ``add_finished_callback``, ``add_response_callback``, ``route_path``, ``route_url``, and ``static_url`` methods to ``pyramid.request.Request`` API docs. - Add (minimal) documentation about using I18N within Mako templates to "Internationalization and Localization" narrative chapter. - Move content of "Forms" chapter back to "Views" chapter; I can't think of a better place to put it. - Slightly improved interface docs for ``IAuthorizationPolicy``. - Minimally explain usage of custom regular expressions in URL dispatch replacement markers within URL Dispatch chapter. Deprecations ------------- - Using the ``pyramid.view.bfg_view`` alias for ``pyramid.view.view_config`` (a backwards compatibility shim) now issues a deprecation warning. Backwards Incompatibilities --------------------------- - Using ``testing.setUp`` now registers an ISettings utility as a side effect. Some test code which queries for this utility after ``testing.setUp`` via queryAdapter will expect a return value of ``None``. This code will need to be changed. - When a ``pyramid.exceptions.Forbidden`` error is raised, its status code now ``403 Forbidden``. It was previously ``401 Unauthorized``, for backwards compatibility purposes with ``repoze.bfg``. This change will cause problems for users of Pyramid with ``repoze.who``, which intercepts ``401 Unauthorized`` by default, but allows ``403 Forbidden`` to pass through. Those deployments will need to configure ``repoze.who`` to also react to ``403 Forbidden``. - The default value for the ``cookie_on_exception`` parameter to ``pyramid.session.UnencyrptedCookieSessionFactory`` is now ``True``. This means that when view code causes an exception to be raised, and the session has been mutated, a cookie will be sent back in the response. Previously its default value was ``False``. Paster Templates ---------------- - The ``pyramid_zodb``, ``pyramid_routesalchemy`` and ``pyramid_alchemy`` paster templates now use a default "commit veto" hook when configuring the ``repoze.tm2`` transaction manager in ``development.ini``. This prevents a transaction from being committed when the response status code is within the 400 or 500 ranges. See also http://docs.repoze.org/tm2/#using-a-commit-veto. 1.0a10 (2011-01-18) =================== Bug Fixes --------- - URL dispatch now properly handles a ``.*`` or ``*`` appearing in a regex match when used inside brackets. Resolves issue #90. Backwards Incompatibilities --------------------------- - The ``add_handler`` method of a Configurator has been removed from the Pyramid core. Handlers are now a feature of the ``pyramid_handlers`` package, which can be downloaded from PyPI. Documentation for the package should be available via http://pylonsproject.org/projects/pyramid_handlers/dev/, which describes how to add a configuration statement to your ``main`` block to reobtain this method. You will also need to add an ``install_requires`` dependency upon ``pyramid_handlers`` to your ``setup.py`` file. - The ``load_zcml`` method of a Configurator has been removed from the Pyramid core. Loading ZCML is now a feature of the ``pyramid_zcml`` package, which can be downloaded from PyPI. Documentation for the package should be available via http://pylonsproject.org/projects/pyramid_zcml/dev/, which describes how to add a configuration statement to your ``main`` block to reobtain this method. You will also need to add an ``install_requires`` dependency upon ``pyramid_zcml`` to your ``setup.py`` file. - The ``pyramid.includes`` subpackage has been removed. ZCML files which use include the package ``pyramid.includes`` (e.g. ````) now must include the ``pyramid_zcml`` package instead (e.g. ````). - The ``pyramid.view.action`` decorator has been removed from the Pyramid core. Handlers are now a feature of the ``pyramid_handlers`` package. It should now be imported from ``pyramid_handlers`` e.g. ``from pyramid_handlers import action``. - The ``handler`` ZCML directive has been removed. It is now a feature of the ``pyramid_handlers`` package. - The ``pylons_minimal``, ``pylons_basic`` and ``pylons_sqla`` paster templates were removed. Use ``pyramid_sqla`` (available from PyPI) as a generic replacement for Pylons-esque development. - The ``make_app`` function has been removed from the ``pyramid.router`` module. It continues life within the ``pyramid_zcml`` package. This leaves the ``pyramid.router`` module without any API functions. - The ``configure_zcml`` setting within the deployment settings (within ``**settings`` passed to a Pyramid ``main`` function) has ceased to have any meaning. Features -------- - ``pyramid.testing.setUp`` and ``pyramid.testing.tearDown`` have been undeprecated. They are now the canonical setup and teardown APIs for test configuration, replacing "direct" creation of a Configurator. This is a change designed to provide a facade that will protect against any future Configurator deprecations. - Add ``charset`` attribute to ``pyramid.testing.DummyRequest`` (unconditionally ``UTF-8``). - Add ``add_directive`` method to configurator, which allows framework extenders to add methods to the configurator (ala ZCML directives). - When ``Configurator.include`` is passed a *module* as an argument, it defaults to attempting to find and use a callable named ``includeme`` within that module. This makes it possible to use ``config.include('some.module')`` rather than ``config.include('some.module.somefunc')`` as long as the include function within ``some.module`` is named ``includeme``. - The ``bfg2pyramid`` script now converts ZCML include tags that have ``repoze.bfg.includes`` as a package attribute to the value ``pyramid_zcml``. For example, ```` will be converted to ````. Paster Templates ---------------- - All paster templates now use ``pyramid.testing.setUp`` and ``pyramid.testing.tearDown`` rather than creating a Configurator "by hand" within their ``tests.py`` module, as per decision in features above. - The ``starter_zcml`` paster template has been moved to the ``pyramid_zcml`` package. Documentation ------------- - The wiki and wiki2 tutorials now use ``pyramid.testing.setUp`` and ``pyramid.testing.tearDown`` rather than creating a Configurator "by hand", as per decision in features above. - The "Testing" narrative chapter now explains ``pyramid.testing.setUp`` and ``pyramid.testing.tearDown`` instead of Configurator creation and ``Configurator.begin()`` and ``Configurator.end()``. - Document the ``request.override_renderer`` attribute within the narrative "Renderers" chapter in a section named "Overriding A Renderer at Runtime". - The "Declarative Configuration" narrative chapter has been removed (it was moved to the ``pyramid_zcml`` package). - Most references to ZCML in narrative chapters have been removed or redirected to ``pyramid_zcml`` locations. Deprecations ------------ - Deprecation warnings related to import of the following API functions were added: ``pyramid.traversal.find_model``, ``pyramid.traversal.model_path``, ``pyramid.traversal.model_path_tuple``, ``pyramid.url.model_url``. The instructions emitted by the deprecation warnings instruct the developer to change these method spellings to their ``resource`` equivalents. This is a consequence of the mass concept rename of "model" to "resource" performed in 1.0a7. 1.0a9 (2011-01-08) ================== Bug Fixes --------- - The ``proutes`` command tried too hard to resolve the view for printing, resulting in exceptions when an exceptional root factory was encountered. Instead of trying to resolve the view, if it cannot, it will now just print ````. - The `self` argument was included in new methods of the ``ISession`` interface signature, causing ``pyramid_beaker`` tests to fail. - Readd ``pyramid.traversal.model_path_tuple`` as an alias for ``pyramid.traversal.resource_path_tuple`` for backwards compatibility. Features -------- - Add a new API ``pyramid.url.current_route_url``, which computes a URL based on the "current" route (if any) and its matchdict values. - ``config.add_view`` now accepts a ``decorator`` keyword argument, a callable which will decorate the view callable before it is added to the registry. - If a handler class provides an ``__action_decorator__`` attribute (usually a classmethod or staticmethod), use that as the decorator for each view registration for that handler. - The ``pyramid.interfaces.IAuthenticationPolicy`` interface now specifies an ``unauthenticated_userid`` method. This method supports an important optimization required by people who are using persistent storages which do not support object caching and whom want to create a "user object" as a request attribute. - A new API has been added to the ``pyramid.security`` module named ``unauthenticated_userid``. This API function calls the ``unauthenticated_userid`` method of the effective security policy. - An ``unauthenticated_userid`` method has been added to the dummy authentication policy returned by ``pyramid.config.Configurator.testing_securitypolicy``. It returns the same thing as that the dummy authentication policy's ``authenticated_userid`` method. - The class ``pyramid.authentication.AuthTktCookieHelper`` is now an API. This class can be used by third-party authentication policy developers to help in the mechanics of authentication cookie-setting. - New constructor argument to Configurator: ``default_view_mapper``. Useful to create systems that have alternate view calling conventions. A view mapper allows objects that are meant to be used as view callables to have an arbitrary argument list and an arbitrary result. The object passed as ``default_view_mapper`` should implement the ``pyramid.interfaces.IViewMapperFactory`` interface. - add a ``set_view_mapper`` API to Configurator. Has the same result as passing ``default_view_mapper`` to the Configurator constructor. - ``config.add_view`` now accepts a ``mapper`` keyword argument, which should either be ``None``, a string representing a Python dotted name, or an object which is an ``IViewMapperFactory``. This feature is not useful for "civilians", only for extension writers. - Allow static renderer provided during view registration to be overridden at request time via a request attribute named ``override_renderer``, which should be the name of a previously registered renderer. Useful to provide "omnipresent" RPC using existing rendered views. - Instances of ``pyramid.testing.DummyRequest`` now have a ``session`` object, which is mostly a dictionary, but also implements the other session API methods for flash and CSRF. Backwards Incompatibilities --------------------------- - Since the ``pyramid.interfaces.IAuthenticationPolicy`` interface now specifies that a policy implementation must implement an ``unauthenticated_userid`` method, all third-party custom authentication policies now must implement this method. It, however, will only be called when the global function named ``pyramid.security.unauthenticated_userid`` is invoked, so if you're not invoking that, you will not notice any issues. - ``pyramid.interfaces.ISession.get_csrf_token`` now mandates that an implementation should return a *new* token if one doesn't already exist in the session (previously it would return None). The internal sessioning implementation has been changed. Documentation ------------- - The (weak) "Converting a CMF Application to Pyramid" tutorial has been removed from the tutorials section. It was moved to the ``pyramid_tutorials`` Github repository. - The "Resource Location and View Lookup" chapter has been replaced with a variant of Rob Miller's "Much Ado About Traversal" (originally published at http://blog.nonsequitarian.org/2010/much-ado-about-traversal/). - Many minor wording tweaks and refactorings (merged Casey Duncan's docs fork, in which he is working on general editing). - Added (weak) description of new view mapper feature to Hooks narrative chapter. - Split views chapter into 2: View Callables and View Configuration. - Reorder Renderers and Templates chapters after View Callables but before View Configuration. - Merge Session Objects, Cross-Site Request Forgery, and Flash Messaging chapter into a single Sessions chapter. - The Wiki and Wiki2 tutorials now have much nicer CSS and graphics. Internals --------- - The "view derivation" code is now factored into a set of classes rather than a large number of standalone functions (a side effect of the view mapper refactoring). - The ``pyramid.renderer.RendererHelper`` class has grown a ``render_view`` method, which is used by the default view mapper (a side effect of the view mapper refactoring). - The object passed as ``renderer`` to the "view deriver" is now an instance of ``pyramid.renderers.RendererHelper`` rather than a dictionary (a side effect of view mapper refactoring). - The class used as the "page template" in ``pyramid.chameleon_text`` was removed, in preference to using a Chameleon-inbuilt version. - A view callable wrapper registered in the registry now contains an ``__original_view__`` attribute which references the original view callable (or class). - The (non-API) method of all internal authentication policy implementations previously named ``_get_userid`` is now named ``unauthenticated_userid``, promoted to an API method. If you were overriding this method, you'll now need to override it as ``unauthenticated_userid`` instead. - Remove (non-API) function of config.py named _map_view. 1.0a8 (2010-12-27) ================== Bug Fixes --------- - The name ``registry`` was not available in the ``paster pshell`` environment under IPython. Features -------- - If a resource implements a ``__resource_url__`` method, it will be called as the result of invoking the ``pyramid.url.resource_url`` function to generate a URL, overriding the default logic. See the new "Generating The URL Of A Resource" section within the Resources narrative chapter. - Added flash messaging, as described in the "Flash Messaging" narrative documentation chapter. - Added CSRF token generation, as described in the narrative chapter entitled "Preventing Cross-Site Request Forgery Attacks". - Prevent misunderstanding of how the ``view`` and ``view_permission`` arguments to add_route work by raising an exception during configuration if view-related arguments exist but no ``view`` argument is passed. - Add ``paster proute`` command which displays a summary of the routing table. See the narrative documentation section within the "URL Dispatch" chapter entitled "Displaying All Application Routes". Paster Templates ---------------- - The ``pyramid_zodb`` Paster template no longer employs ZCML. Instead, it is based on scanning. Documentation ------------- - Added "Generating The URL Of A Resource" section to the Resources narrative chapter (includes information about overriding URL generation using ``__resource_url__``). - Added "Generating the Path To a Resource" section to the Resources narrative chapter. - Added "Finding a Resource by Path" section to the Resources narrative chapter. - Added "Obtaining the Lineage of a Resource" to the Resources narrative chapter. - Added "Determining if a Resource is In The Lineage of Another Resource" to Resources narrative chapter. - Added "Finding the Root Resource" to Resources narrative chapter. - Added "Finding a Resource With a Class or Interface in Lineage" to Resources narrative chapter. - Added a "Flash Messaging" narrative documentation chapter. - Added a narrative chapter entitled "Preventing Cross-Site Request Forgery Attacks". - Changed the "ZODB + Traversal Wiki Tutorial" based on changes to ``pyramid_zodb`` Paster template. - Added "Advanced Configuration" narrative chapter which documents how to deal with configuration conflicts, two-phase configuration, ``include`` and ``commit``. - Fix API documentation rendering for ``pyramid.view.static`` - Add "Pyramid Provides More Than One Way to Do It" to Design Defense documentation. - Changed "Static Assets" narrative chapter: clarify that ``name`` represents a prefix unless it's a URL, added an example of a root-relative static view fallback for URL dispatch, added an example of creating a simple view that returns the body of a file. - Move ZCML usage in Hooks chapter to Declarative Configuration chapter. - Merge "Static Assets" chapter into the "Assets" chapter. - Added narrative documentation section within the "URL Dispatch" chapter entitled "Displaying All Application Routes" (for ``paster proutes`` command). 1.0a7 (2010-12-20) ================== Terminology Changes ------------------- - The Pyramid concept previously known as "model" is now known as "resource". As a result: - The following API changes have been made:: pyramid.url.model_url -> pyramid.url.resource_url pyramid.traversal.find_model -> pyramid.url.find_resource pyramid.traversal.model_path -> pyramid.traversal.resource_path pyramid.traversal.model_path_tuple -> pyramid.traversal.resource_path_tuple pyramid.traversal.ModelGraphTraverser -> pyramid.traversal.ResourceTreeTraverser pyramid.config.Configurator.testing_models -> pyramid.config.Configurator.testing_resources pyramid.testing.registerModels -> pyramid.testing.registerResources pyramid.testing.DummyModel -> pyramid.testing.DummyResource - All documentation which previously referred to "model" now refers to "resource". - The ``starter`` and ``starter_zcml`` paster templates now have a ``resources.py`` module instead of a ``models.py`` module. - Positional argument names of various APIs have been changed from ``model`` to ``resource``. Backwards compatibility shims have been left in place in all cases. They will continue to work "forever". - The Pyramid concept previously known as "resource" is now known as "asset". As a result: - The (non-API) module previously known as ``pyramid.resource`` is now known as ``pyramid.asset``. - All docs that previously referred to "resource specification" now refer to "asset specification". - The following API changes were made:: pyramid.config.Configurator.absolute_resource_spec -> pyramid.config.Configurator.absolute_asset_spec pyramid.config.Configurator.override_resource -> pyramid.config.Configurator.override_asset - The ZCML directive previously known as ``resource`` is now known as ``asset``. - The setting previously known as ``BFG_RELOAD_RESOURCES`` (envvar) or ``reload_resources`` (config file) is now known, respectively, as ``PYRAMID_RELOAD_ASSETS`` and ``reload_assets``. Backwards compatibility shims have been left in place in all cases. They will continue to work "forever". Bug Fixes --------- - Make it possible to succesfully run all tests via ``nosetests`` command directly (rather than indirectly via ``python setup.py nosetests``). - When a configuration conflict is encountered during scanning, the conflict exception now shows the decorator information that caused the conflict. Features -------- - Added ``debug_routematch`` configuration setting that logs matched routes (including the matchdict and predicates). - The name ``registry`` is now available in a ``pshell`` environment by default. It is the application registry object. Environment ----------- - All environment variables which used to be prefixed with ``BFG_`` are now prefixed with ``PYRAMID_`` (e.g. ``BFG_DEBUG_NOTFOUND`` is now ``PYRAMID_DEBUG_NOTFOUND``) Documentation ------------- - Added "Debugging Route Matching" section to the urldispatch narrative documentation chapter. - Added reference to ``PYRAMID_DEBUG_ROUTEMATCH`` envvar and ``debug_routematch`` config file setting to the Environment narrative docs chapter. - Changed "Project" chapter slightly to expand on use of ``paster pshell``. - Direct Jython users to Mako rather than Jinja2 in "Install" narrative chapter. - Many changes to support terminological renaming of "model" to "resource" and "resource" to "asset". - Added an example of ``WebTest`` functional testing to the testing narrative chapter. - Rearranged chapter ordering by popular demand (URL dispatch first, then traversal). Put hybrid chapter after views chapter. - Split off "Renderers" as its own chapter from "Views" chapter in narrative documentation. Paster Templates ---------------- - Added ``debug_routematch = false`` to all paster templates. Dependencies ------------ - Depend on Venusian >= 0.5 (for scanning conflict exception decoration). 1.0a6 (2010-12-15) ================== Bug Fixes --------- - 1.0a5 introduced a bug when ``pyramid.config.Configurator.scan`` was used without a ``package`` argument (e.g. ``config.scan()`` as opposed to ``config.scan('packagename')``. The symptoms were: lots of deprecation warnings printed to the console about imports of deprecated Pyramid functions and classes and non-detection of view callables decorated with ``view_config`` decorators. This has been fixed. - Tests now pass on Windows (no bugs found, but a few tests in the test suite assumed UNIX path segments in filenames). Documentation ------------- - If you followed it to-the-letter, the ZODB+Traversal Wiki tutorial would instruct you to run a test which would fail because the view callable generated by the ``pyramid_zodb`` tutorial used a one-arg view callable, but the test in the sample code used a two-arg call. - Updated ZODB+Traversal tutorial setup.py of all steps to match what's generated by ``pyramid_zodb``. - Fix reference to ``repoze.bfg.traversalwrapper`` in "Models" chapter (point at ``pyramid_traversalwrapper`` instead). 1.0a5 (2010-12-14) ================== Features -------- - Add a ``handler`` ZCML directive. This directive does the same thing as ``pyramid.configuration.add_handler``. - A new module named ``pyramid.config`` was added. It subsumes the duties of the older ``pyramid.configuration`` module. - The new ``pyramid.config.Configurator` class has API methods that the older ``pyramid.configuration.Configurator`` class did not: ``with_context`` (a classmethod), ``include``, ``action``, and ``commit``. These methods exist for imperative application extensibility purposes. - The ``pyramid.testing.setUp`` function now accepts an ``autocommit`` keyword argument, which defaults to ``True``. If it is passed ``False``, the Config object returned by ``setUp`` will be a non-autocommiting Config object. - Add logging configuration to all paster templates. - ``pyramid_alchemy``, ``pyramid_routesalchemy``, and ``pylons_sqla`` paster templates now use idiomatic SQLAlchemy configuration in their respective ``.ini`` files and Python code. - ``pyramid.testing.DummyRequest`` now has a class variable, ``query_string``, which defaults to the empty string. - Add support for json on GAE by catching NotImplementedError and importing simplejson from django.utils. - The Mako renderer now accepts a resource specification for ``mako.module_directory``. - New boolean Mako settings variable ``mako.strict_undefined``. See `Mako Context Variables `_ for its meaning. Dependencies ------------ - Depend on Mako 0.3.6+ (we now require the ``strict_undefined`` feature). Bug Fixes --------- - When creating a Configurator from within a ``paster pshell`` session, you were required to pass a ``package`` argument although ``package`` is not actually required. If you didn't pass ``package``, you would receive an error something like ``KeyError: '__name__'`` emanating from the ``pyramid.path.caller_module`` function. This has now been fixed. - The ``pyramid_routesalchemy`` paster template's unit tests failed (``AssertionError: 'SomeProject' != 'someproject'``). This is fixed. - Make default renderer work (renderer factory registered with no name, which is active for every view unless the view names a specific renderer). - The Mako renderer did not properly turn the ``mako.imports``, ``mako.default_filters``, and ``mako.imports`` settings into lists. - The Mako renderer did not properly convert the ``mako.error_handler`` setting from a dotted name to a callable. Documentation ------------- - Merged many wording, readability, and correctness changes to narrative documentation chapters from https://github.com/caseman/pyramid (up to and including "Models" narrative chapter). - "Sample Applications" section of docs changed to note existence of Cluegun, Shootout and Virginia sample applications, ported from their repoze.bfg origin packages. - SQLAlchemy+URLDispatch tutorial updated to integrate changes to ``pyramid_routesalchemy`` template. - Add ``pyramid.interfaces.ITemplateRenderer`` interface to Interfaces API chapter (has ``implementation()`` method, required to be used when getting at Chameleon macros). - Add a "Modifying Package Structure" section to the project narrative documentation chapter (explain turning a module into a package). - Documentation was added for the new ``handler`` ZCML directive in the ZCML section. Deprecations ------------ - ``pyramid.configuration.Configurator`` is now deprecated. Use ``pyramid.config.Configurator``, passing its constructor ``autocommit=True`` instead. The ``pyramid.configuration.Configurator`` alias will live for a long time, as every application uses it, but its import now issues a deprecation warning. The ``pyramid.config.Configurator`` class has the same API as ``pyramid.configuration.Configurator`` class, which it means to replace, except by default it is a *non-autocommitting* configurator. The now-deprecated ``pyramid.configuration.Configurator`` will autocommit every time a configuration method is called. The ``pyramid.configuration`` module remains, but it is deprecated. Use ``pyramid.config`` instead. 1.0a4 (2010-11-21) ================== Features -------- - URL Dispatch now allows for replacement markers to be located anywhere in the pattern, instead of immediately following a ``/``. - URL Dispatch now uses the form ``{marker}`` to denote a replace marker in the route pattern instead of ``:marker``. The old colon-style marker syntax is still accepted for backwards compatibility. The new format allows a regular expression for that marker location to be used instead of the default ``[^/]+``, for example ``{marker:\d+}`` is now valid to require the marker to be digits. - Add a ``pyramid.url.route_path`` API, allowing folks to generate relative URLs. Calling ``route_path`` is the same as calling ``pyramid.url.route_url`` with the argument ``_app_url`` equal to the empty string. - Add a ``pyramid.request.Request.route_path`` API. This is a convenience method of the request which calls ``pyramid.url.route_url``. - Make test suite pass on Jython (requires PasteScript trunk, presumably to be 1.7.4). - Make test suite pass on PyPy (Chameleon doesn't work). - Surrounding application configuration with ``config.begin()`` and ``config.end()`` is no longer necessary. All paster templates have been changed to no longer call these functions. - Fix configurator to not convert ``ImportError`` to ``ConfigurationError`` if the import that failed was unrelated to the import requested via a dotted name when resolving dotted names (such as view dotted names). Documentation ------------- - SQLAlchemy+URLDispatch and ZODB+Traversal tutorials have been updated to not call ``config.begin()`` or ``config.end()``. Bug Fixes --------- - Add deprecation warnings to import of ``pyramid.chameleon_text`` and ``pyramid.chameleon_zpt`` of ``get_renderer``, ``get_template``, ``render_template``, and ``render_template_to_response``. - Add deprecation warning for import of ``pyramid.zcml.zcml_configure`` and ``pyramid.zcml.file_configure``. - The ``pyramid_alchemy`` paster template had a typo, preventing an import from working. - Fix apparent failures when calling ``pyramid.traversal.find_model(root, path)`` or ``pyramid.traversal.traverse(path)`` when ``path`` is (erroneously) a Unicode object. The user is meant to pass these APIs a string object, never a Unicode object. In practice, however, users indeed pass Unicode. Because the string that is passed must be ASCII encodeable, now, if they pass a Unicode object, its data is eagerly converted to an ASCII string rather than being passed along to downstream code as a convenience to the user and to prevent puzzling second-order failures from cropping up (all failures will occur within ``pyramid.traversal.traverse`` rather than later down the line as the result of calling e.g. ``traversal_path``). Backwards Incompatibilities --------------------------- - The ``pyramid.testing.zcml_configure`` API has been removed. It had been advertised as removed since repoze.bfg 1.2a1, but hadn't actually been. Deprecations ------------ - The ``pyramid.settings.get_settings`` API is now deprecated. Use ``pyramid.threadlocals.get_current_registry().settings`` instead or use the ``settings`` attribute of the registry available from the request (``request.registry.settings``). Documentation ------------- - Removed ``zodbsessions`` tutorial chapter. It's still useful, but we now have a SessionFactory abstraction which competes with it, and maintaining documentation on both ways to do it is a distraction. Internal -------- - Replace Twill with WebTest in internal integration tests (avoid deprecation warnings generated by Twill). 1.0a3 (2010-11-16) ================== Features -------- - Added Mako TemplateLookup settings for ``mako.error_handler``, ``mako.default_filters``, and ``mako.imports``. - Normalized all paster templates: each now uses the name ``main`` to represent the function that returns a WSGI application, each now uses WebError, each now has roughly the same shape of development.ini style. - Added class vars ``matchdict`` and ``matched_route`` to ``pyramid.request.Request``. Each is set to ``None``. - New API method: ``pyramid.settings.asbool``. - New API methods for ``pyramid.request.Request``: ``model_url``, ``route_url``, and ``static_url``. These are simple passthroughs for their respective functions in ``pyramid.url``. - The ``settings`` object which used to be available only when ``request.settings.get_settings`` was called is now available as ``registry.settings`` (e.g. ``request.registry.settings`` in view code). Bug Fixes --------- - The pylons_* paster templates erroneously used the ``{squiggly}`` routing syntax as the pattern supplied to ``add_route``. This style of routing is not supported. They were replaced with ``:colon`` style route patterns. - The pylons_* paster template used the same string (``your_app_secret_string``) for the ``session.secret`` setting in the generated ``development.ini``. This was a security risk if left unchanged in a project that used one of the templates to produce production applications. It now uses a randomly generated string. Documentation ------------- - ZODB+traversal wiki (``wiki``) tutorial updated due to changes to ``pyramid_zodb`` paster template. - SQLAlchemy+urldispach wiki (``wiki2``) tutorial updated due to changes to ``pyramid_routesalchemy`` paster template. - Documented the ``matchdict`` and ``matched_route`` attributes of the request object in the Request API documentation. Deprecations ------------ - Obtaining the ``settings`` object via ``registry.{get|query}Utility(ISettings)`` is now deprecated. Instead, obtain the ``settings`` object via the ``registry.settings`` attribute. A backwards compatibility shim was added to the registry object to register the settings object as an ISettings utility when ``setattr(registry, 'settings', foo)`` is called, but it will be removed in a later release. - Obtaining the ``settings`` object via ``pyramid.settings.get_settings`` is now deprecated. Obtain it as the ``settings`` attribute of the registry now (obtain the registry via ``pyramid.threadlocal.get_registry`` or as ``request.registry``). Behavior Differences -------------------- - Internal: ZCML directives no longer call get_current_registry() if there's a ``registry`` attribute on the ZCML context (kill off use of threadlocals). - Internal: Chameleon template renderers now accept two arguments: ``path`` and ``lookup``. ``Lookup`` will be an instance of a lookup class which supplies (late-bound) arguments for debug, reload, and translate. Any third-party renderers which use (the non-API) function ``pyramid.renderers.template_renderer_factory`` will need to adjust their implementations to obey the new callback argument list. This change was to kill off inappropriate use of threadlocals. 1.0a2 (2010-11-09) ================== Documentation ------------- - All references to events by interface (e.g. ``pyramid.interfaces.INewRequest``) have been changed to reference their concrete classes (e.g. ``pyramid.events.NewRequest``) in documentation about making subscriptions. - All references to Pyramid-the-application were changed from mod-`pyramid` to app-`Pyramid`. A custom role setting was added to ``docs/conf.py`` to allow for this. (internal) 1.0a1 (2010-11-05) ================== Features (delta from BFG 1.3) ------------------------------- - Mako templating renderer supports resource specification format for template lookups and within Mako templates. Absolute filenames must be used in Pyramid to avoid this lookup process. - Add ``pyramid.httpexceptions`` module, which is a facade for the ``webob.exc`` module. - Direct built-in support for the Mako templating language. - A new configurator method exists: ``add_handler``. This method adds a Pylons-style "view handler" (such a thing used to be called a "controller" in Pylons 1.0). - New argument to configurator: ``session_factory``. - New method on configurator: ``set_session_factory`` - Using ``request.session`` now returns a (dictionary-like) session object if a session factory has been configured. - The request now has a new attribute: ``tmpl_context`` for benefit of Pylons users. - The decorator previously known as ``pyramid.view.bfg_view`` is now known most formally as ``pyramid.view.view_config`` in docs and paster templates. An import of ``pyramid.view.bfg_view``, however, will continue to work "forever". - New API methods in ``pyramid.session``: ``signed_serialize`` and ``signed_deserialize``. - New interface: ``pyramid.interfaces.IRendererInfo``. An object of this type is passed to renderer factory constructors (see "Backwards Incompatibilities"). - New event type: ``pyramid.interfaces.IBeforeRender``. An object of this type is sent as an event before a renderer is invoked (but after the application-level renderer globals factory added via ``pyramid.configurator.configuration.set_renderer_globals_factory``, if any, has injected its own keys). Applications may now subscribe to the ``IBeforeRender`` event type in order to introspect the and modify the set of renderer globals before they are passed to a renderer. The event object iself has a dictionary-like interface that can be used for this purpose. For example:: from repoze.events import subscriber from pyramid.interfaces import IRendererGlobalsEvent @subscriber(IRendererGlobalsEvent) def add_global(event): event['mykey'] = 'foo' If a subscriber attempts to add a key that already exist in the renderer globals dictionary, a ``KeyError`` is raised. This limitation is due to the fact that subscribers cannot be ordered relative to each other. The set of keys added to the renderer globals dictionary by all subscribers and app-level globals factories must be unique. - New class: ``pyramid.response.Response``. This is a pure facade for ``webob.Response`` (old code need not change to use this facade, it's existence is mostly for vanity and documentation-generation purposes). - All preexisting paster templates (except ``zodb``) now use "imperative" configuration (``starter``, ``routesalchemy``, ``alchemy``). - A new paster template named ``pyramid_starter_zcml`` exists, which uses declarative configuration. Documentation (delta from BFG 1.3) ----------------------------------- - Added a ``pyramid.httpexceptions`` API documentation chapter. - Added a ``pyramid.session`` API documentation chapter. - Added a ``Session Objects`` narrative documentation chapter. - Added an API chapter for the ``pyramid.personality`` module. - Added an API chapter for the ``pyramid.response`` module. - All documentation which previously referred to ``webob.Response`` now uses ``pyramid.response.Response`` instead. - The documentation has been overhauled to use imperative configuration, moving declarative configuration (ZCML) explanations to a separate narrative chapter ``declarative.rst``. - The ZODB Wiki tutorial was updated to take into account changes to the ``pyramid_zodb`` paster template. - The SQL Wiki tutorial was updated to take into account changes to the ``pyramid_routesalchemy`` paster template. Backwards Incompatibilities (with BFG 1.3) ------------------------------------------ - There is no longer an ``IDebugLogger`` registered as a named utility with the name ``repoze.bfg.debug``. - The logger which used to have the name of ``repoze.bfg.debug`` now has the name ``pyramid.debug``. - The deprecated API ``pyramid.testing.registerViewPermission`` has been removed. - The deprecated API named ``pyramid.testing.registerRoutesMapper`` has been removed. - The deprecated API named ``pyramid.request.get_request`` was removed. - The deprecated API named ``pyramid.security.Unauthorized`` was removed. - The deprecated API named ``pyramid.view.view_execution_permitted`` was removed. - The deprecated API named ``pyramid.view.NotFound`` was removed. - The ``bfgshell`` paster command is now named ``pshell``. - The Venusian "category" for all built-in Venusian decorators (e.g. ``subscriber`` and ``view_config``/``bfg_view``) is now ``pyramid`` instead of ``bfg``. - ``pyramid.renderers.rendered_response`` function removed; use ``render_pyramid.renderers.render_to_response`` instead. - Renderer factories now accept a *renderer info object* rather than an absolute resource specification or an absolute path. The object has the following attributes: ``name`` (the ``renderer=`` value), ``package`` (the 'current package' when the renderer configuration statement was found), ``type``: the renderer type, ``registry``: the current registry, and ``settings``: the deployment settings dictionary. Third-party ``repoze.bfg`` renderer implementations that must be ported to Pyramid will need to account for this. This change was made primarily to support more flexible Mako template rendering. - The presence of the key ``repoze.bfg.message`` in the WSGI environment when an exception occurs is now deprecated. Instead, code which relies on this environ value should use the ``exception`` attribute of the request (e.g. ``request.exception[0]``) to retrieve the message. - The values ``bfg_localizer`` and ``bfg_locale_name`` kept on the request during internationalization for caching purposes were never APIs. These however have changed to ``localizer`` and ``locale_name``, respectively. - The default ``cookie_name`` value of the ``authtktauthenticationpolicy`` ZCML now defaults to ``auth_tkt`` (it used to default to ``repoze.bfg.auth_tkt``). - The default ``cookie_name`` value of the ``pyramid.authentication.AuthTktAuthenticationPolicy`` constructor now defaults to ``auth_tkt`` (it used to default to ``repoze.bfg.auth_tkt``). - The ``request_type`` argument to the ``view`` ZCML directive, the ``pyramid.configuration.Configurator.add_view`` method, or the ``pyramid.view.view_config`` decorator (nee ``bfg_view``) is no longer permitted to be one of the strings ``GET``, ``HEAD``, ``PUT``, ``POST`` or ``DELETE``, and now must always be an interface. Accepting the method-strings as ``request_type`` was a backwards compatibility strategy servicing repoze.bfg 1.0 applications. Use the ``request_method`` parameter instead to specify that a view a string request-method predicate. pyramid-1.4.5/.gitmodules0000664000175000017500000000014712203711415014635 0ustar takakitakaki[submodule "docs/_themes"] path = docs/_themes url = git://github.com/Pylons/pylons_sphinx_theme.git pyramid-1.4.5/COPYRIGHT.txt0000664000175000017500000000041112203712502014561 0ustar takakitakakiCopyright (c) 2008-2011 Agendaless Consulting and Contributors. (http://www.agendaless.com), All Rights Reserved Portions (c) Zope Foundation and contributors (http://www.zope.org/). Portions (c) Edgewall Software (http://edgewall.org) Portions (c) Ian Bicking. pyramid-1.4.5/BFG_HISTORY.txt0000664000175000017500000074176512210154276015066 0ustar takakitakaki1.3b1 (2010-10-25) ================== Features -------- - The ``paster`` template named ``bfg_routesalchemy`` has been updated to use SQLAlchemy declarative syntax. Thanks to Ergo^. Bug Fixes --------- - When a renderer factory could not be found, a misleading error message was raised if the renderer name was not a string. Documentation ------------- - The ""bfgwiki2" (SQLAlchemy + url dispatch) tutorial has been updated slightly. In particular, the source packages no longer attempt to use a private index, and the recommended Python version is now 2.6. It was also updated to take into account the changes to the ``bfg_routesalchemy`` template used to set up an environment. - The "bfgwiki" (ZODB + traversal) tutorial has been updated slightly. In particular, the source packages no longer attempt to use a private index, and the recommended Python version is now 2.6. 1.3a15 (2010-09-30) =================== Features -------- - The ``repoze.bfg.traversal.traversal_path`` API now eagerly attempts to encode a Unicode ``path`` into ASCII before attempting to split it and decode its segments. This is for convenience, effectively to allow a (stored-as-Unicode-in-a-database, or retrieved-as-Unicode-from-a-request-parameter) Unicode path to be passed to ``find_model``, which eventually internally uses the ``traversal_path`` function under the hood. In version 1.2 and prior, if the ``path`` was Unicode, that Unicode was split on slashes and each resulting segment value was Unicode. An inappropriate call to the ``decode()`` method of a resulting Unicode path segment could cause a ``UnicodeDecodeError`` to occur even if the Unicode representation of the path contained no 'high order' characters (it effectively did a "double decode"). By converting the Unicode path argument to ASCII before we attempt to decode and split, genuine errors will occur in a more obvious place while also allowing us to handle (for convenience) the case that it's a Unicode representation formed entirely from ASCII-compatible characters. 1.3a14 (2010-09-14) =================== Bug Fixes --------- - If an exception view was registered through the legacy ``set_notfound_view`` or ``set_forbidden_view`` APIs, the context sent to the view was incorrect (could be ``None`` inappropriately). Features -------- - Compatibility with WebOb 1.0. Requirements ------------ - Now requires WebOb >= 1.0. Backwards Incompatibilities --------------------------- - Due to changes introduced WebOb 1.0, the ``repoze.bfg.request.make_request_ascii`` event subscriber no longer works, so it has been removed. This subscriber was meant to be used in a deployment so that code written before BFG 0.7.0 could run unchanged. At this point, such code will need to be rewritten to expect Unicode from ``request.GET``, ``request.POST`` and ``request.params`` or it will need to be changed to use ``request.str_POST``, ``request.str_GET`` and/or ``request.str_params`` instead of the non-``str`` versions of same, as the non-``str`` versions of the same APIs always now perform decoding to Unicode. Errata ------ - A prior changelog entry asserted that the ``INewResponse`` event was not sent to listeners if the response was not "valid" (if a view or renderer returned a response object that did not have a status/headers/app_iter). This is not true in this release, nor was it true in 1.3a13. 1.3a13 (2010-09-14) =================== Bug Fixes --------- - The ``traverse`` route predicate could not successfully generate a traversal path. Features -------- - In support of making it easier to configure applications which are "secure by default", a default permission feature was added. If supplied, the default permission is used as the permission string to all view registrations which don't otherwise name a permission. These APIs are in support of that: - A new constructor argument was added to the Configurator: ``default_permission``. - A new method was added to the Configurator: ``set_default_permission``. - A new ZCML directive was added: ``default_permission``. - Add a new request API: ``request.add_finished_callback``. Finished callbacks are called by the router unconditionally near the very end of request processing. See the "Using Finished Callbacks" section of the "Hooks" narrative chapter of the documentation for more information. - A ``request.matched_route`` attribute is now added to the request when a route has matched. Its value is the "route" object that matched (see the ``IRoute`` interface within ``repoze.bfg.interfaces`` API documentation for the API of a route object). - The ``exception`` attribute of the request is now set slightly earlier and in a slightly different set of scenarios, for benefit of "finished callbacks" and "response callbacks". In previous versions, the ``exception`` attribute of the request was not set at all if an exception view was not found. In this version, the ``request.exception`` attribute is set immediately when an exception is caught by the router, even if an exception view could not be found. - The ``add_route`` method of a Configurator now accepts a ``pregenerator`` argument. The pregenerator for the resulting route is called by ``route_url`` in order to adjust the set of arguments passed to it by the user for special purposes, such as Pylons 'subdomain' support. It will influence the URL returned by ``route_url``. See the ``repoze.bfg.interfaces.IRoutePregenerator`` interface for more information. Backwards Incompatibilities --------------------------- - The router no longer sets the value ``wsgiorg.routing_args`` into the environ when a route matches. The value used to be something like ``((), matchdict)``. This functionality was only ever obliquely referred to in change logs; it was never documented as an API. - The ``exception`` attribute of the request now defaults to ``None``. In prior versions, the ``request.exception`` attribute did not exist if an exception was not raised by user code during request processing; it only began existence once an exception view was found. Deprecations ------------ - The ``repoze.bfg.interfaces.IWSGIApplicationCreatedEvent`` event interface was renamed to ``repoze.bfg.interfaces.IApplicationCreated``. Likewise, the ``repoze.bfg.events.WSGIApplicationCreatedEvent`` class was renamed to ``repoze.bfg.events.ApplicationCreated``. The older aliases will continue to work indefinitely. - The ``repoze.bfg.interfaces.IAfterTraversal`` event interface was renamed to ``repoze.bfg.interfaces.IContextFound``. Likewise, the ``repoze.bfg.events.AfterTraversal`` class was renamed to ``repoze.bfg.events.ContextFound``. The older aliases will continue to work indefinitely. - References to the WSGI environment values ``bfg.routes.matchdict`` and ``bfg.routes.route`` were removed from documentation. These will stick around internally for several more releases, but it is ``request.matchdict`` and ``request.matched_route`` are now the "official" way to obtain the matchdict and the route object which resulted in the match. Documentation ------------- - Added documentation for the ``default_permission`` ZCML directive. - Added documentation for the ``default_permission`` constructor value and the ``set_default_permission`` method in the Configurator API documentation. - Added a new section to the "security" chapter named "Setting a Default Permission". - Document ``renderer_globals_factory`` and ``request_factory`` arguments to Configurator constructor. - Added two sections to the "Hooks" chapter of the documentation: "Using Response Callbacks" and "Using Finished Callbacks". - Added documentation of the ``request.exception`` attribute to the ``repoze.bfg.request.Request`` API documentation. - Added glossary entries for "response callback" and "finished callback". - The "Request Processing" narrative chapter has been updated to note finished and response callback steps. - New interface in interfaces API documentation: ``IRoutePregenerator``. - Added a "The Matched Route" section to the URL Dispatch narrative docs chapter, detailing the ``matched_route`` attribute. 1.3a12 (2010-09-08) =================== Bug Fixes --------- - Fix a bug in ``repoze.bfg.url.static_url`` URL generation: if two resource specifications were used to create two separate static views, but they shared a common prefix, it was possible that ``static_url`` would generate an incorrect URL. - Fix another bug in ``repoze.bfg.static_url`` URL generation: too many slashes in generated URL. - Prevent a race condition which could result in a ``RuntimeError`` when rendering a Chameleon template that has not already been rendered once. This would usually occur directly after a restart, when more than one person or thread is trying to execute the same view at the same time: https://bugs.launchpad.net/karl3/+bug/621364 Features -------- - The argument to ``repoze.bfg.configuration.Configurator.add_route`` which was previously called ``path`` is now called ``pattern`` for better explicability. For backwards compatibility purposes, passing a keyword argument named ``path`` to ``add_route`` will still work indefinitely. - The ``path`` attribute to the ZCML ``route`` directive is now named ``pattern`` for better explicability. The older ``path`` attribute will continue to work indefinitely. Documentation ------------- - All narrative, API, and tutorial docs which referred to a route pattern as a ``path`` have now been updated to refer to them as a ``pattern``. - The ``repoze.bfg.interfaces`` API documentation page is now rendered via ``repoze.sphinx.autointerface``. - The URL Dispatch narrative chapter now refers to the ``interfaces`` chapter to explain the API of an ``IRoute`` object. Paster Templates ---------------- - The routesalchemy template has been updated to use ``pattern`` in its route declarations rather than ``path``. Dependencies ------------ - ``tests_require`` now includes ``repoze.sphinx.autointerface`` as a dependency. Internal -------- - Add an API to the ``Configurator`` named ``get_routes_mapper``. This returns an object implementing the ``IRoutesMapper`` interface. - The ``repoze.bfg.urldispatch.RoutesMapper`` object now has a ``get_route`` method which returns a single Route object or ``None``. - A new interface ``repoze.bfg.interfaces.IRoute`` was added. The ``repoze.bfg.urldispatch.Route`` object implements this interface. - The canonical attribute for accessing the routing pattern from a route object is now ``pattern`` rather than ``path``. - Use ``hash()`` rather than ``id()`` when computing the "phash" of a custom route/view predicate in order to allow the custom predicate some control over which predicates are "equal". - Use ``response.headerlist.append`` instead of ``response.headers.add`` in ``repoze.bfg.request.add_global_response_headers`` in case the response is not a WebOb response. - The ``repoze.bfg.urldispatch.Route`` constructor (not an API) now accepts a different ordering of arguments. Previously it was ``(pattern, name, factory=None, predicates=())``. It is now ``(name, pattern, factory=None, predicates=())``. This is in support of consistency with ``configurator.add_route``. - The ``repoze.bfg.urldispatch.RoutesMapper.connect`` method (not an API) now accepts a different ordering of arguments. Previously it was ``(pattern, name, factory=None, predicates=())``. It is now ``(name, pattern, factory=None, predicates=())``. This is in support of consistency with ``configurator.add_route``. 1.3a11 (2010-09-05) =================== Bug Fixes --------- - Process the response callbacks and the NewResponse event earlier, to enable mutations to the response to take effect. 1.3a10 (2010-09-05) =================== Features -------- - A new ``repoze.bfg.request.Request.add_response_callback`` API has been added. This method is documented in the new ``repoze.bfg.request`` API chapter. It can be used to influence response values before a concrete response object has been created. - The ``repoze.bfg.interfaces.INewResponse`` interface now includes a ``request`` attribute; as a result, a handler for INewResponse now has access to the request which caused the response. - Each of the follow methods of the Configurator now allow the below-named arguments to be passed as "dotted name strings" (e.g. "foo.bar.baz") rather than as actual implementation objects that must be imported: setup_registry root_factory, authentication_policy, authorization_policy, debug_logger, locale_negotiator, request_factory, renderer_globals_factory add_subscriber subscriber, iface derive_view view add_view view, ``for_``, context, request_type, containment add_route() view, view_for, factory, ``for_``, view_context scan package add_renderer factory set_forbidden_view view set_notfound_view view set_request_factory factory set_renderer_globals_factory() factory set_locale_negotiator negotiator testing_add_subscriber event_iface Bug Fixes --------- - The route pattern registered internally for a local "static view" (either via the ``static`` ZCML directive or via the ``add_static_view`` method of the configurator) was incorrect. It was regsistered for e.g. ``static*traverse``, while it should have been registered for ``static/*traverse``. Symptom: two static views could not reliably be added to a system when they both shared the same path prefix (e.g. ``/static`` and ``/static2``). Backwards Incompatibilities --------------------------- - The INewResponse event is now not sent to listeners if the response returned by view code (or a renderer) is not a "real" response (e.g. if it does not have ``.status``, ``.headerlist`` and ``.app_iter`` attribtues). Documentation ------------- - Add an API chapter for the ``repoze.bfg.request`` module, which includes documentation for the ``repoze.bfg.request.Request`` class (the "request object"). - Modify the "Request and Response" narrative chapter to reference the new ``repoze.bfg.request`` API chapter. Some content was moved from this chapter into the API documentation itself. - Various changes to denote that Python dotted names are now allowed as input to Configurator methods. Internal -------- - The (internal) feature which made it possible to attach a ``global_response_headers`` attribute to the request (which was assumed to contain a sequence of header key/value pairs which would later be added to the response by the router), has been removed. The functionality of ``repoze.bfg.request.Request.add_response_callback`` takes its place. - The ``repoze.bfg.events.NewResponse`` class's construct has changed: it now must be created with ``(request, response)`` rather than simply ``(response)``. 1.3a9 (2010-08-22) ================== Features -------- - The Configurator now accepts a dotted name *string* to a package as a ``package`` constructor argument. The ``package`` argument was previously required to be a package *object* (not a dotted name string). - The ``repoze.bfg.configuration.Configurator.with_package`` method was added. This method returns a new Configurator using the same application registry as the configurator object it is called upon. The new configurator is created afresh with its ``package`` constructor argument set to the value passed to ``with_package``. This feature will make it easier for future BFG versions to allow dotted names as arguments in places where currently only object references are allowed (the work to allow dotted names isntead of object references everywhere has not yet been done, however). - The new ``repoze.bfg.configuration.Configurator.maybe_dotted`` method resolves a Python dotted name string supplied as its ``dotted`` argument to a global Python object. If the value cannot be resolved, a ``repoze.bfg.configuration.ConfigurationError`` is raised. If the value supplied as ``dotted`` is not a string, the value is returned unconditionally without any resolution attempted. - The new ``repoze.bfg.configuration.Configurator.absolute_resource_spec`` method resolves a potentially relative "resource specification" string into an absolute version. If the value supplied as ``relative_spec`` is not a string, the value is returned unconditionally without any resolution attempted. Backwards Incompatibilities --------------------------- - The functions in ``repoze.bfg.renderers`` named ``render`` and ``render_to_response`` introduced in 1.3a6 previously took a set of ``**values`` arguments for the values to be passed to the renderer. This was wrong, as renderers don't need to accept only dictionaries (they can accept any type of object). Now, the value sent to the renderer must be supplied as a positional argument named ``value``. The ``request`` argument is still a keyword argument, however. - The functions in ``repoze.bfg.renderers`` named ``render`` and ``render_to_response`` now accept an additonal keyword argument named ``package``. - The ``get_renderer`` API in ``repoze.bfg.renderers`` now accepts a ``package`` argument. Documentation ------------- - The ZCML ``include`` directive docs were incorrect: they specified ``filename`` rather than (the correct) ``file`` as an allowable attribute. Internal -------- - The ``repoze.bfg.resource.resolve_resource_spec`` function can now accept a package object as its ``pname`` argument instead of just a package name. - The ``_renderer_factory_from_name`` and ``_renderer_from_name`` methods of the Configurator were removed. These were never APIs. - The ``_render``, ``_render_to_response`` and ``_make_response`` functions with ``repoze.bfg.render`` (added in 1.3a6) have been removed. - A new helper class ``repoze.bfg.renderers.RendererHelper`` was added. - The _map_view function of ``repoze.bfg.configuration`` now takes only a renderer_name argument instead of both a ``renderer`` and ``renderer``_name argument. It also takes a ``package`` argument now. - Use ``imp.get_suffixes`` indirection in ``repoze.bfg.path.package_name`` instead of hardcoded ``.py`` ``.pyc`` and ``.pyo`` to use for comparison when attemtping to decide if a directory is a package. - Make tests runnable again under Jython (although they do not all pass currently). - The reify decorator now maintains the docstring of the function it wraps. 1.3a8 (2010-08-08) ================== Features -------- - New public interface: ``repoze.bfg.exceptions.IExceptionResponse``. This interface is provided by all internal exception classes (such as ``repoze.bfg.exceptions.NotFound`` and ``repoze.bfg.exceptions.Forbidden``), instances of which are both exception objects and can behave as WSGI response objects. This interface is made public so that exception classes which are also valid WSGI response factories can be configured to implement them or exception instances which are also or response instances can be configured to provide them. - New API class: ``repoze.bfg.view.AppendSlashNotFoundViewFactory``. There can only be one Not Found view in any ``repoze.bfg`` application. Even if you use ``repoze.bfg.view.append_slash_notfound_view`` as the Not Found view, ``repoze.bfg`` still must generate a ``404 Not Found`` response when it cannot redirect to a slash-appended URL; this not found response will be visible to site users. If you don't care what this 404 response looks like, and you only need redirections to slash-appended route URLs, you may use the ``repoze.bfg.view.append_slash_notfound_view`` object as the Not Found view. However, if you wish to use a *custom* notfound view callable when a URL cannot be redirected to a slash-appended URL, you may wish to use an instance of the ``repoze.bfg.view.AppendSlashNotFoundViewFactory`` class as the Not Found view, supplying the notfound view callable as the first argument to its constructor. For instance:: from repoze.bfg.exceptions import NotFound from repoze.bfg.view import AppendSlashNotFoundViewFactory def notfound_view(context, request): return HTTPNotFound('It aint there, stop trying!') custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) config.add_view(custom_append_slash, context=NotFound) The ``notfound_view`` supplied must adhere to the two-argument view callable calling convention of ``(context, request)`` (``context`` will be the exception object). Documentation -------------- - Expanded the "Cleaning Up After a Request" section of the URL Dispatch narrative chapter. - Expanded the "Redirecting to Slash-Appended Routes" section of the URL Dispatch narrative chapter. Internal -------- - Previously, two default view functions were registered at Configurator setup (one for ``repoze.bfg.exceptions.NotFound`` named ``default_notfound_view`` and one for ``repoze.bfg.exceptions.Forbidden`` named ``default_forbidden_view``) to render internal exception responses. Those default view functions have been removed, replaced with a generic default view function which is registered at Configurator setup for the ``repoze.bfg.interfaces.IExceptionResponse`` interface that simply returns the exception instance; the ``NotFound`` and ``Forbidden`` classes are now still exception factories but they are also response factories which generate instances that implement the new ``repoze.bfg.interfaces.IExceptionResponse`` interface. 1.3a7 (2010-08-01) ================== Features -------- - The ``repoze.bfg.configuration.Configurator.add_route`` API now returns the route object that was added. - A ``repoze.bfg.events.subscriber`` decorator was added. This decorator decorates module-scope functions, which are then treated as event listeners after a scan() is performed. See the Events narrative documentation chapter and the ``repoze.bfg.events`` module documentation for more information. Bug Fixes --------- - When adding a view for a route which did not yet exist ("did not yet exist" meaning, temporally, a view was added with a route name for a route which had not yet been added via add_route), the value of the ``custom_predicate`` argument to ``add_view`` was lost. Symptom: wrong view matches when using URL dispatch and custom view predicates together. - Pattern matches for a ``:segment`` marker in a URL dispatch route pattern now always match at least one character. See "Backwards Incompatibilities" below in this changelog. Backwards Incompatibilities --------------------------- - A bug existed in the regular expression to do URL matching. As an example, the URL matching machinery would cause the pattern ``/{foo}`` to match the root URL ``/`` resulting in a match dictionary of ``{'foo':u''}`` or the pattern ``/{fud}/edit might match the URL ``//edit`` resulting in a match dictionary of ``{'fud':u''}``. It was always the intent that ``:segment`` markers in the pattern would need to match *at least one* character, and never match the empty string. This, however, means that in certain circumstances, a routing match which your application inadvertently depended upon may no longer happen. Documentation -------------- - Added description of the ``repoze.bfg.events.subscriber`` decorator to the Events narrative chapter. - Added ``repoze.bfg.events.subscriber`` API documentation to ``repoze.bfg.events`` API docs. - Added a section named "Zope 3 Enforces 'TTW' Authorization Checks By Default; BFG Does Not" to the "Design Defense" chapter. 1.3a6 (2010-07-25) ================== Features -------- - New argument to ``repoze.bfg.configuration.Configurator.add_route`` and the ``route`` ZCML directive: ``traverse``. If you would like to cause the ``context`` to be something other than the ``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 ``path``. For example, if the ``path`` provided is ``articles/:article/edit``, and the ``traverse`` argument provided 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 ``context`` of the request. The Traversal narrative has more information about traversal. If the traversal path contains segment marker names which are not present in the path argument, a runtime error will occur. The ``traverse`` pattern should not contain segment markers that do not exist in the ``path``. A similar combining of routing and traversal is available when a route is matched which contains a ``*traverse`` remainder marker in its path. The ``traverse`` argument 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 is ignored when attached to a route that has a ``*traverse`` remainder marker in its path. - A new method of the ``Configurator`` exists: ``set_request_factory``. If used, this method will set the factory used by the ``repoze.bfg`` router to create all request objects. - The ``Configurator`` constructor takes an additional argument: ``request_factory``. If used, this argument will set the factory used by the ``repoze.bfg`` router to create all request objects. - The ``Configurator`` constructor takes an additional argument: ``request_factory``. If used, this argument will set the factory used by the ``repoze.bfg`` router to create all request objects. - A new method of the ``Configurator`` exists: ``set_renderer_globals_factory``. If used, this method will set the factory used by the ``repoze.bfg`` router to create renderer globals. - A new method of the ``Configurator`` exists: ``get_settings``. If used, this method will return the current settings object (performs the same job as the ``repoze.bfg.settings.get_settings`` API). - The ``Configurator`` constructor takes an additional argument: ``renderer_globals_factory``. If used, this argument will set the factory used by the ``repoze.bfg`` router to create renderer globals. - Add ``repoze.bfg.renderers.render``, ``repoze.bfg.renderers.render_to_response`` and ``repoze.bfg.renderers.get_renderer`` functions. These are imperative APIs which will use the same rendering machinery used by view configurations with a ``renderer=`` attribute/argument to produce a rendering or renderer. Because these APIs provide a central API for all rendering, they now form the preferred way to perform imperative template rendering. Using functions named ``render_*`` from modules such as ``repoze.bfg.chameleon_zpt`` and ``repoze.bfg.chameleon_text`` is now discouraged (although not deprecated). The code the backing older templating-system-specific APIs now calls into the newer ``repoze.bfg.renderer`` code. - The ``repoze.bfg.configuration.Configurator.testing_add_template`` has been renamed to ``testing_add_renderer``. A backwards compatibility alias is present using the old name. Documentation ------------- - The ``Hybrid`` narrative chapter now contains a description of the ``traverse`` route argument. - The ``Hooks`` narrative chapter now contains sections about changing the request factory and adding a renderer globals factory. - The API documentation includes a new module: ``repoze.bfg.renderers``. - The ``Templates`` chapter was updated; all narrative that used templating-specific APIs within examples to perform rendering (such as the ``repoze.bfg.chameleon_zpt.render_template_to_response`` method) was changed to use ``repoze.bfg.renderers.render_*`` functions. Bug Fixes --------- - The ``header`` predicate (when used as either a view predicate or a route predicate) had a problem when specified with a name/regex pair. When the header did not exist in the headers dictionary, the regex match could be fed ``None``, causing it to throw a ``TypeError: expected string or buffer`` exception. Now, the predicate returns False as intended. Deprecations ------------ - The ``repoze.bfg.renderers.rendered_response`` function was never an official API, but may have been imported by extensions in the wild. It is officially deprecated in this release. Use ``repoze.bfg.renderers.render_to_response`` instead. - The following APIs are *documentation* deprecated (meaning they are officially deprecated in documentation but do not raise a deprecation error upon their usage, and may continue to work for an indefinite period of time): In the ``repoze.bfg.chameleon_zpt`` module: ``get_renderer``, ``get_template``, ``render_template``, ``render_template_to_response``. The suggested alternatives are documented within the docstrings of those methods (which are still present in the documentation). In the ``repoze.bfg.chameleon_text`` module: ``get_renderer``, ``get_template``, ``render_template``, ``render_template_to_response``. The suggested alternatives are documented within the docstrings of those methods (which are still present in the documentation). In general, to perform template-related functions, one should now use the various methods in the ``repoze.bfg.renderers`` module. Backwards Incompatibilities --------------------------- - A new internal exception class (*not* an API) named ``repoze.bfg.exceptions.PredicateMismatch`` now exists. This exception is currently raised when no constituent view of a multiview can be called (due to no predicate match). Previously, in this situation, a ``repoze.bfg.exceptions.NotFound`` was raised. We provide backwards compatibility for code that expected a ``NotFound`` to be raised when no predicates match by causing ``repoze.bfg.exceptions.PredicateMismatch`` to inherit from ``NotFound``. This will cause any exception view registered for ``NotFound`` to be called when a predicate mismatch occurs, as was the previous behavior. There is however, one perverse case that will expose a backwards incompatibility. If 1) you had a view that was registered as a member of a multiview 2) this view explicitly raised a ``NotFound`` exception *in order to* proceed to the next predicate check in the multiview, that code will now behave differently: rather than skipping to the next view match, a NotFound will be raised to the top-level exception handling machinery instead. For code to be depending upon the behavior of a view raising ``NotFound`` to proceed to the next predicate match, would be tragic, but not impossible, given that ``NotFound`` is a public interface. ``repoze.bfg.exceptions.PredicateMismatch`` is not a public API and cannot be depended upon by application code, so you should not change your view code to raise ``PredicateMismatch``. Instead, move the logic which raised the ``NotFound`` exception in the view out into a custom view predicate. - If, when you run your application's unit test suite under BFG 1.3, a ``KeyError`` naming a template or a ``ValueError`` indicating that a 'renderer factory' is not registered may is raised (e.g. ``ValueError: No factory for renderer named '.pt' when looking up karl.views:templates/snippets.pt``), you may need to perform some extra setup in your test code. The best solution is to use the ``repoze.bfg.configuration.Configurator.testing_add_renderer`` (or, alternately the deprecated ``repoze.bfg.testing.registerTemplateRenderer`` or ``registerDummyRenderer``) API within the code comprising each individual unit test suite to register a "dummy" renderer for each of the templates and renderers used by code under test. For example:: config = Configurator() config.testing_add_renderer('karl.views:templates/snippets.pt') This will register a basic dummy renderer for this particular missing template. The ``testing_add_renderer`` API actually *returns* the renderer, but if you don't care about how the render is used, you don't care about having a reference to it either. A more rough way to solve the issue exists. It causes the "real" template implementations to be used while the system is under test, which is suboptimal, because tests will run slower, and unit tests won't actually *be* unit tests, but it is easier. Always ensure you call the ``setup_registry()`` method of the Configurator . Eg:: reg = MyRegistry() config = Configurator(registry=reg) config.setup_registry() Calling ``setup_registry`` only has an effect if you're *passing in* a ``registry`` argument to the Configurator constructor. ``setup_registry`` is called by the course of normal operations anyway if you do not pass in a ``registry``. If your test suite isn't using a Configurator yet, and is still using the older ``repoze.bfg.testing`` APIs name ``setUp`` or ``cleanUp``, these will register the renderers on your behalf. A variant on the symptom for this theme exists: you may already be dutifully registering a dummy template or renderer for a template used by the code you're testing using ``testing_register_renderer`` or ``registerTemplateRenderer``, but (perhaps unbeknownst to you) the code under test expects to be able to use a "real" template renderer implementation to retrieve or render *another* template that you forgot was being rendered as a side effect of calling the code you're testing. This happened to work because it found the *real* template while the system was under test previously, and now it cannot. The solution is the same. It may also help reduce confusion to use a *resource specification* to specify the template path in the test suite and code rather than a relative path in either. A resource specification is unambiguous, while a relative path needs to be relative to "here", where "here" isn't always well-defined ("here" in a test suite may or may not be the same as "here" in the code under test). 1.3a5 (2010-07-14) ================== Features -------- - New internal exception: ``repoze.bfg.exceptions.URLDecodeError``. This URL is a subclass of the built-in Python exception named ``UnicodeDecodeError``. - When decoding a URL segment to Unicode fails, the exception raised is now ``repoze.bfg.exceptions.URLDecodeError`` instead of ``UnicodeDecodeError``. This makes it possible to register an exception view invoked specifically when ``repoze.bfg`` cannot decode a URL. Bug Fixes --------- - Fix regression in ``repoze.bfg.configuration.Configurator.add_static_view``. Before 1.3a4, view names that contained a slash were supported as route prefixes. 1.3a4 broke this by trying to treat them as full URLs. Documentation ------------- - The ``repoze.bfg.exceptions.URLDecodeError`` exception was added to the exceptions chapter of the API documentation. Backwards Incompatibilities ---------------------------- - in previous releases, when a URL could not be decoded from UTF-8 during traversal, a ``TypeError`` was raised. Now the error which is raised is a ``repoze.bfg.exceptions.URLDecodeError``. 1.3a4 (2010-07-03) ================== Features -------- - Undocumented hook: make ``get_app`` and ``get_root`` of the ``repoze.bfg.paster.BFGShellCommand`` hookable in cases where endware may interfere with the default versions. - In earlier versions, a custom route predicate associated with a url dispatch route (each of the predicate functions fed to the ``custom_predicates`` argument of ``repoze.bfg.configuration.Configurator.add_route``) has always required a 2-positional argument signature, e.g. ``(context, request)``. Before this release, the ``context`` argument was always ``None``. As of this release, the first argument passed to a predicate is now a dictionary conventionally named ``info`` consisting of ``route``, and ``match``. ``match`` is a dictionary: it represents the arguments matched in the URL by the route. ``route`` is an object representing the route which was matched. This is useful when predicates need access to the route match. For example:: def any_of(segment_name, *args): def predicate(info, request): if info['match'][segment_name] in args: return True return predicate num_one_two_or_three = any_of('num, 'one', 'two', 'three') add_route('num', '/:num', custom_predicates=(num_one_two_or_three,)) The ``route`` object is an object that has two useful attributes: ``name`` and ``path``. The ``name`` attribute is the route name. The ``path`` attribute is the route pattern. An example of using the route in a set of route predicates:: def twenty_ten(info, request): if info['route'].name in ('ymd', 'ym', 'y'): return info['match']['year'] == '2010' add_route('y', '/:year', custom_predicates=(twenty_ten,)) add_route('ym', '/:year/:month', custom_predicates=(twenty_ten,)) add_route('ymd', '/:year/:month:/day', custom_predicates=(twenty_ten,)) - The ``repoze.bfg.url.route_url`` API has changed. If a keyword ``_app_url`` is present in the arguments passed to ``route_url``, this value will be used as the protocol/hostname/port/leading path prefix of the generated URL. For example, using an ``_app_url`` of ``http://example.com:8080/foo`` would cause the URL ``http://example.com:8080/foo/fleeb/flub`` to be returned from this function if the expansion of the route pattern associated with the ``route_name`` expanded to ``/fleeb/flub``. - It is now possible to use a URL as the ``name`` argument fed to ``repoze.bfg.configuration.Configurator.add_static_view``. When the name argument is a URL, the ``repoze.bfg.url.static_url`` API will generate join this URL (as a prefix) to a path including the static file name. This makes it more possible to put static media on a separate webserver for production, while keeping static media package-internal and served by the development webserver during development. Documentation ------------- - The authorization chapter of the ZODB Wiki Tutorial (docs/tutorials/bfgwiki) was changed to demonstrate authorization via a group rather than via a direct username (thanks to Alex Marandon). - The authorization chapter of the SQLAlchemy Wiki Tutorial (docs/tutorials/bfgwiki2) was changed to demonstrate authorization via a group rather than via a direct username. - Redirect requests for tutorial sources to http://docs.repoze.org/bfgwiki-1.3 and http://docs.repoze.org/bfgwiki2-1.3/ respectively. - A section named ``Custom Route Predicates`` was added to the URL Dispatch narrative chapter. - The Static Resources chapter has been updated to mention using ``static_url`` to generate URLs to external webservers. Internal -------- - Removed ``repoze.bfg.static.StaticURLFactory`` in favor of a new abstraction revolving around the (still-internal) ``repoze.bfg.static.StaticURLInfo`` helper class. 1.3a3 (2010-05-01) ================== Paster Templates ---------------- - The ``bfg_alchemy`` and ``bfg_routesalchemy`` templates no longer register a ``handle_teardown`` event listener which calls ``DBSession.remove``. This was found by Chris Withers to be unnecessary. Documentation ------------- - The "bfgwiki2" (URL dispatch wiki) tutorial code and documentation was changed to remove the ``handle_teardown`` event listener which calls ``DBSession.remove``. - Any mention of the ``handle_teardown`` event listener as used by the paster templates was removed from the URL Dispatch narrative chapter. - A section entitled Detecting Available Languages was added to the i18n narrative docs chapter. 1.3a2 (2010-04-28) ================== Features -------- - A locale negotiator no longer needs to be registered explicitly. The default locale negotiator at ``repoze.bfg.i18n.default_locale_negotiator`` is now used unconditionally as... um, the default locale negotiator. - The default locale negotiator has become more complex. * First, the negotiator looks for the ``_LOCALE_`` attribute of the request object (possibly set by a view or an event listener). * Then it looks for the ``request.params['_LOCALE_']`` value. * Then it looks for the ``request.cookies['_LOCALE_']`` value. Backwards Incompatibilities --------------------------- - The default locale negotiator now looks for the parameter named ``_LOCALE_`` rather than a parameter named ``locale`` in ``request.params``. Behavior Changes ---------------- - A locale negotiator may now return ``None``, signifying that the default locale should be used. Documentation ------------- - Documentation concerning locale negotiation in the Internationalizationa and Localization chapter was updated. - Expanded portion of i18n narrative chapter docs which discuss working with gettext files. 1.3a1 (2010-04-26) ================== Features -------- - Added "exception views". When you use an exception (anything that inherits from the Python ``Exception`` builtin) as view context argument, e.g.:: from repoze.bfg.view import bfg_view from repoze.bfg.exceptions import NotFound from webob.exc import HTTPNotFound @bfg_view(context=NotFound) def notfound_view(request): return HTTPNotFound() For the above example, when the ``repoze.bfg.exceptions.NotFound`` exception is raised by any view or any root factory, the ``notfound_view`` view callable will be invoked and its response returned. Other normal view predicates can also be used in combination with an exception view registration:: from repoze.bfg.view import bfg_view from repoze.bfg.exceptions import NotFound from webob.exc import HTTPNotFound @bfg_view(context=NotFound, route_name='home') def notfound_view(request): return HTTPNotFound() The above exception view names the ``route_name`` of ``home``, meaning that it will only be called when the route matched has a name of ``home``. You can therefore have more than one exception view for any given exception in the system: the "most specific" one will be called when the set of request circumstances which match the view registration. The only predicate that cannot be not be used successfully is ``name``. The name used to look up an exception view is always the empty string. Existing (pre-1.3) normal views registered against objects inheriting from ``Exception`` will continue to work. Exception views used for user-defined exceptions and system exceptions used as contexts will also work. The feature can be used with any view registration mechanism (``@bfg_view`` decorator, ZCML, or imperative ``config.add_view`` styles). This feature was kindly contributed by Andrey Popp. - Use "Venusian" (`http://docs.repoze.org/venusian `_) to perform ``bfg_view`` decorator scanning rather than relying on a BFG-internal decorator scanner. (Truth be told, Venusian is really just a generalization of the BFG-internal decorator scanner). - Internationalization and localization features as documented in the narrative documentation chapter entitled ``Internationalization and Localization``. - A new deployment setting named ``default_locale_name`` was added. If this string is present as a Paster ``.ini`` file option, it will be considered the default locale name. The default locale name is used during locale-related operations such as language translation. - It is now possible to turn on Chameleon template "debugging mode" for all Chameleon BFG templates by setting a BFG-related Paster ``.ini`` file setting named ``debug_templates``. The exceptions raised by Chameleon templates when a rendering fails are sometimes less than helpful. ``debug_templates`` allows you to configure your application development environment so that exceptions generated by Chameleon during template compilation and execution will contain more helpful debugging information. This mode is on by default in all new projects. - Add a new method of the Configurator named ``derive_view`` which can be used to generate a BFG view callable from a user-supplied function, instance, or class. This useful for external framework and plugin authors wishing to wrap callables supplied by their users which follow the same calling conventions and response conventions as objects that can be supplied directly to BFG as a view callable. See the ``derive_view`` method in the ``repoze.bfg.configuration.Configurator`` docs. ZCML ---- - Add a ``translationdir`` ZCML directive to support localization. - Add a ``localenegotiator`` ZCML directive to support localization. Deprecations ------------ - The exception views feature replaces the need for the ``set_notfound_view`` and ``set_forbidden_view`` methods of the ``Configurator`` as well as the ``notfound`` and ``forbidden`` ZCML directives. Those methods and directives will continue to work for the foreseeable future, but they are deprecated in the documentation. Dependencies ------------ - A new install-time dependency on the ``venusian`` distribution was added. - A new install-time dependency on the ``translationstring`` distribution was added. - Chameleon 1.2.3 or better is now required (internationalization and per-template debug settings). Internal -------- - View registrations and lookups are now done with three "requires" arguments instead of two to accomodate orthogonality of exception views. - The ``repoze.bfg.interfaces.IForbiddenView`` and ``repoze.bfg.interfaces.INotFoundView`` interfaces were removed; they weren't APIs and they became vestigial with the addition of exception views. - Remove ``repoze.bfg.compat.pkgutil_26.py`` and import alias ``repoze.bfg.compat.walk_packages``. These were only required by internal scanning machinery; Venusian replaced the internal scanning machinery, so these are no longer required. Documentation ------------- - Exception view documentation was added to the ``Hooks`` narrative chapter. - A new narrative chapter entitled ``Internationalization and Localization`` was added. - The "Environment Variables and ``ini`` File Settings" chapter was changed: documentation about the ``default_locale_name`` setting was added. - A new API chapter for the ``repoze.bfg.i18n`` module was added. - Documentation for the new ``translationdir`` and ``localenegotiator`` ZCML directives were added. - A section was added to the Templates chapter entitled "Nicer Exceptions in Templates" describing the result of setting ``debug_templates = true``. Paster Templates ---------------- - All paster templates now create a ``setup.cfg`` which includes commands related to nose testing and Babel message catalog extraction/compilation. - A ``default_locale_name = en`` setting was added to each existing paster template. - A ``debug_templates = true`` setting was added to each existing paster template. Licensing --------- - The Edgewall (BSD) license was added to the LICENSES.txt file, as some code in the ``repoze.bfg.i18n`` derives from Babel source. 1.2 (2010-02-10) ================ - No changes from 1.2b6. 1.2b6 (2010-02-06) ================== Backwards Incompatibilities --------------------------- - Remove magical feature of ``repoze.bfg.url.model_url`` which prepended a fully-expanded urldispatch route URL before a the model's path if it was noticed that the request had matched a route. This feature was ill-conceived, and didn't work in all scenarios. Bug Fixes --------- - More correct conversion of provided ``renderer`` values to resource specification values (internal). 1.2b5 (2010-02-04) ================== Bug Fixes --------- - 1.2b4 introduced a bug whereby views added via a route configuration that named a view callable and also a ``view_attr`` became broken. Symptom: ``MyViewClass is not callable`` or the ``__call__`` of a class was being called instead of the method named via ``view_attr``. - Fix a bug whereby a ``renderer`` argument to the ``@bfg_view`` decorator that provided a package-relative template filename might not have been resolved properly. Symptom: inappropriate ``Missing template resource`` errors. 1.2b4 (2010-02-03) ================== Documentation ------------- - Update GAE tutorial to use Chameleon instead of Jinja2 (now that it's possible). Bug Fixes --------- - Ensure that ``secure`` flag for AuthTktAuthenticationPolicy constructor does what it's documented to do (merge Daniel Holth's fancy-cookies-2 branch). Features -------- - Add ``path`` and ``http_only`` options to AuthTktAuthenticationPolicy constructor (merge Daniel Holth's fancy-cookies-2 branch). Backwards Incompatibilities --------------------------- - Remove ``view_header``, ``view_accept``, ``view_xhr``, ``view_path_info``, ``view_request_method``, ``view_request_param``, and ``view_containment`` predicate arguments from the ``Configurator.add_route`` argument list. These arguments were speculative. If you need the features exposed by these arguments, add a view associated with a route using the ``route_name`` argument to the ``add_view`` method instead. - Remove ``view_header``, ``view_accept``, ``view_xhr``, ``view_path_info``, ``view_request_method``, ``view_request_param``, and ``view_containment`` predicate arguments from the ``route`` ZCML directive attribute set. These attributes were speculative. If you need the features exposed by these attributes, add a view associated with a route using the ``route_name`` attribute of the ``view`` ZCML directive instead. Dependencies ------------ - Remove dependency on ``sourcecodegen`` (not depended upon by Chameleon 1.1.1+). 1.2b3 (2010-01-24) ================== Bug Fixes --------- - When "hybrid mode" (both traversal and urldispatch) is in use, default to finding route-related views even if a non-route-related view registration has been made with a more specific context. The default used to be to find views with a more specific context first. Use the new ``use_global_views`` argument to the route definition to get back the older behavior. Features -------- - Add ``use_global_views`` argument to ``add_route`` method of Configurator. When this argument is true, views registered for *no* route will be found if no more specific view related to the route is found. - Add ``use_global_views`` attribute to ZCML ```` directive (see above). Internal -------- - When registering a view, register the view adapter with the "requires" interfaces as ``(request_type, context_type)`` rather than ``(context_type, request_type)``. This provides for saner lookup, because the registration will always be made with a specific request interface, but registration may not be made with a specific context interface. In general, when creating multiadapters, you want to order the requires interfaces so that the elements which are more likely to be registered using specific interfaces are ordered before those which are less likely. 1.2b2 (2010-01-21) ================== Bug Fixes --------- - When the ``Configurator`` is passed an instance of ``zope.component.registry.Components`` as a ``registry`` constructor argument, fix the instance up to have the attributes we expect of an instance of ``repoze.bfg.registry.Registry`` when ``setup_registry`` is called. This makes it possible to use the global Zope component registry as a BFG application registry. - When WebOb 0.9.7.1 was used, a deprecation warning was issued for the class attribute named ``charset`` within ``repoze.bfg.request.Request``. BFG now *requires* WebOb >= 0.9.7, and code was added so that this deprecation warning has disappeared. - Fix a view lookup ordering bug whereby a view with a larger number of predicates registered first (literally first, not "earlier") for a triad would lose during view lookup to one registered with fewer. - Make sure views with exactly N custom predicates are always called before views with exactly N non-custom predicates given all else is equal in the view configuration. Documentation ------------- - Change renderings of ZCML directive documentation. - Add a narrative documentation chapter: "Using the Zope Component Architecture in repoze.bfg". Dependencies ------------ - Require WebOb >= 0.9.7 1.2b1 (2010-01-18) ================== Bug Fixes --------- - In ``bfg_routesalchemy``, ``bfg_alchemy`` paster templates and the ``bfgwiki2`` tutorial, clean up the SQLAlchemy connection by registering a ``repoze.tm.after_end`` callback instead of relying on a ``__del__`` method of a ``Cleanup`` class added to the WSGI environment. The ``__del__`` strategy was fragile and caused problems in the wild. Thanks to Daniel Holth for testing. Features -------- - Read logging configuration from PasteDeploy config file ``loggers`` section (and related) when ``paster bfgshell`` is invoked. Documentation ------------- - Major rework in preparation for book publication. 1.2a11 (2010-01-05) =================== Bug Fixes --------- - Make ``paster bfgshell`` and ``paster create -t bfg_xxx`` work on Jython (fix minor incompatibility with treatment of ``__doc__`` at the class level). - Updated dependency on ``WebOb`` to require a version which supports features now used in tests. Features -------- - Jython compatibility (at least when repoze.bfg.jinja2 is used as the templating engine; Chameleon does not work under Jython). - Show the derived abspath of template resource specifications in the traceback when a renderer template cannot be found. - Show the original traceback when a Chameleon template cannot be rendered due to a platform incompatibility. 1.2a10 (2010-01-04) =================== Features -------- - The ``Configurator.add_view`` method now accepts an argument named ``context``. This is an alias for the older argument named ``for_``; it is preferred over ``for_``, but ``for_`` will continue to be supported "forever". - The ``view`` ZCML directive now accepts an attribute named ``context``. This is an alias for the older attribute named ``for``; it is preferred over ``for``, but ``for`` will continue to be supported "forever". - The ``Configurator.add_route`` method now accepts an argument named ``view_context``. This is an alias for the older argument named ``view_for``; it is preferred over ``view_for``, but ``view_for`` will continue to be supported "forever". - The ``route`` ZCML directive now accepts an attribute named ``view_context``. This is an alias for the older attribute named ``view_for``; it is preferred over ``view_for``, but ``view_for`` will continue to be supported "forever". Documentation and Paster Templates ---------------------------------- - LaTeX rendering tweaks. - All uses of the ``Configurator.add_view`` method that used its ``for_`` argument now use the ``context`` argument instead. - All uses of the ``Configurator.add_route`` method that used its ``view_for`` argument now use the ``view_context`` argument instead. - All uses of the ``view`` ZCML directive that used its ``for`` attribute now use the ``context`` attribute instead. - All uses of the ``route`` ZCML directive that used its ``view_for`` attribute now use the ``view_context`` attribute instead. - Add a (minimal) tutorial dealing with use of ``repoze.catalog`` in a ``repoze.bfg`` application. Documentation Licensing ----------------------- - Loosen the documentation licensing to allow derivative works: it is now offered under the `Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License `_. This is only a documentation licensing change; the ``repoze.bfg`` software continues to be offered under the Repoze Public License at http://repoze.org/license.html (BSD-like). 1.2a9 (2009-12-27) ================== Documentation Licensing ----------------------- - The *documentation* (the result of ``make `` within the ``docs`` directory) in this release is now offered under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License as described by http://creativecommons.org/licenses/by-nc-nd/3.0/us/ . This is only a licensing change for the documentation; the ``repoze.bfg`` software continues to be offered under the Repoze Public License at http://repoze.org/license.html (BSD-like). Documentation ------------- - Added manual index entries to generated index. - Document the previously existing (but non-API) ``repoze.bfg.configuration.Configurator.setup_registry`` method as an official API of a ``Configurator``. - Fix syntax errors in various documentation code blocks. - Created new top-level documentation section: "ZCML Directives". This section contains detailed ZCML directive information, some of which was removed from various narrative chapters. - The LaTeX rendering of the documentation has been improved. - Added a "Fore-Matter" section with author, copyright, and licensing information. 1.2a8 (2009-12-24) ================== Features -------- - Add a ``**kw`` arg to the ``Configurator.add_settings`` API. - Add ``hook_zca`` and ``unhook_zca`` methods to the ``Configurator`` API. - The ``repoze.bfg.testing.setUp`` method now returns a ``Configurator`` instance which can be used to do further configuration during unit tests. Bug Fixes --------- - The ``json`` renderer failed to set the response content type to ``application/json``. It now does, by setting ``request.response_content_type`` unless this attribute is already set. - The ``string`` renderer failed to set the response content type to ``text/plain``. It now does, by setting ``request.response_content_type`` unless this attribute is already set. Documentation ------------- - General documentation improvements by using better Sphinx roles such as "class", "func", "meth", and so on. This means that there are many more hyperlinks pointing to API documentation for API definitions in all narrative, tutorial, and API documentation elements. - Added a description of imperative configuration in various places which only described ZCML configuration. - A syntactical refreshing of various tutorials. - Added the ``repoze.bfg.authentication``, ``repoze.bfg.authorization``, and ``repoze.bfg.interfaces`` modules to API documentation. Deprecations ------------ - The ``repoze.bfg.testing.registerRoutesMapper`` API (added in an early 1.2 alpha) was deprecated. Its import now generates a deprecation warning. 1.2a7 (2009-12-20) ================== Features -------- - Add four new testing-related APIs to the ``repoze.bfg.configuration.Configurator`` class: ``testing_securitypolicy``, ``testing_models``, ``testing_add_subscriber``, and ``testing_add_template``. These were added in order to provide more direct access to the functionality of the ``repoze.bfg.testing`` APIs named ``registerDummySecurityPolicy``, ``registerModels``, ``registerEventListener``, and ``registerTemplateRenderer`` when a configurator is used. The ``testing`` APIs named are nominally deprecated (although they will likely remain around "forever", as they are in heavy use in the wild). - Add a new API to the ``repoze.bfg.configuration.Configurator`` class: ``add_settings``. This API can be used to add "settings" (information returned within via the ``repoze.bfg.settings.get_settings`` API) after the configurator has been initially set up. This is most useful for testing purposes. - Add a ``custom_predicates`` argument to the ``Configurator`` ``add_view`` method, the ``bfg_view`` decorator and the attribute list of the ZCML ``view`` directive. If ``custom_predicates`` is specified, it must be a sequence of predicate callables (a predicate callable accepts two arguments: ``context`` and ``request`` and returns ``True`` or ``False``). The associated view callable will only be invoked if all custom predicates return ``True``. Use one or more custom predicates when no existing predefined predicate is useful. Predefined and custom predicates can be mixed freely. - Add a ``custom_predicates`` argument to the ``Configurator`` ``add_route`` and the attribute list of the ZCML ``route`` directive. If ``custom_predicates`` is specified, it must be a sequence of predicate callables (a predicate callable accepts two arguments: ``context`` and ``request`` and returns ``True`` or ``False``). The associated route will match will only be invoked if all custom predicates return ``True``, else route matching continues. Note that the value ``context`` will always be ``None`` when passed to a custom route predicate. Use one or more custom predicates when no existing predefined predicate is useful. Predefined and custom predicates can be mixed freely. Internal -------- - Remove the ``repoze.bfg.testing.registerTraverser`` function. This function was never an API. Documenation ------------ - Doc-deprecated most helper functions in the ``repoze.bfg.testing`` module. These helper functions likely won't be removed any time soon, nor will they generate a warning any time soon, due to their heavy use in the wild, but equivalent behavior exists in methods of a Configurator. 1.2a6 (2009-12-18) ================== Features -------- - The ``Configurator`` object now has two new methods: ``begin`` and ``end``. The ``begin`` method is meant to be called before any "configuration" begins (e.g. before ``add_view``, et. al are called). The ``end`` method is meant to be called after all "configuration" is complete. Previously, before there was imperative configuration at all (1.1 and prior), configuration begin and end was invariably implied by the process of loading a ZCML file. When a ZCML load happened, the threadlocal data structure containing the request and registry was modified before the load, and torn down after the load, making sure that all framework code that needed ``get_current_registry`` for the duration of the ZCML load was satisfied. Some API methods called during imperative configuration, (such as ``Configurator.add_view`` when a renderer is involved) end up for historical reasons calling ``get_current_registry``. However, in 1.2a5 and below, the Configurator supplied no functionality that allowed people to make sure that ``get_current_registry`` returned the registry implied by the configurator being used. ``begin`` now serves this purpose. Inversely, ``end`` pops the thread local stack, undoing the actions of ``begin``. We make this boundary explicit to reduce the potential for confusion when the configurator is used in different circumstances (e.g. in unit tests and app code vs. just in initial app setup). Existing code written for 1.2a1-1.2a5 which does not call ``begin`` or ``end`` continues to work in the same manner it did before. It is however suggested that this code be changed to call ``begin`` and ``end`` to reduce the potential for confusion in the future. - All ``paster`` templates which generate an application skeleton now make use of the new ``begin`` and ``end`` methods of the Configurator they use in their respective copies of ``run.py`` and ``tests.py``. Documentation ------------- - All documentation that makes use of a ``Configurator`` object to do application setup and test setup now makes use of the new ``begin`` and ``end`` methods of the configurator. Bug Fixes --------- - When a ``repoze.bfg.exceptions.NotFound`` or ``repoze.bfg.exceptions.Forbidden`` *class* (as opposed to instance) was raised as an exception within a root factory (or route root factory), the exception would not be caught properly by the ``repoze.bfg.`` Router and it would propagate to up the call stack, as opposed to rendering the not found view or the forbidden view as would have been expected. - When Chameleon page or text templates used as renderers were added imperatively (via ``Configurator.add_view`` or some derivative), they too-eagerly attempted to look up the ``reload_templates`` setting via ``get_settings``, meaning they were always registered in non-auto-reload-mode (the default). Each now waits until its respective ``template`` attribute is accessed to look up the value. - When a route with the same name as a previously registered route was added, the old route was not removed from the mapper's routelist. Symptom: the old registered route would be used (and possibly matched) during route lookup when it should not have had a chance to ever be used. 1.2a5 (2009-12-10) ================== Features -------- - When the ``repoze.bfg.exceptions.NotFound`` or ``repoze.bfg.exceptions.Forbidden`` error is raised from within a custom root factory or the ``factory`` of a route, the appropriate response is now sent to the requesting user agent (the result of the notfound view or the forbidden view, respectively). When these errors are raised from within a root factory, the ``context`` passed to the notfound or forbidden view will be ``None``. Also, the request will not be decorated with ``view_name``, ``subpath``, ``context``, etc. as would normally be the case if traversal had been allowed to take place. Internals --------- - The exception class representing the error raised by various methods of a ``Configurator`` is now importable as ``repoze.bfg.exceptions.ConfigurationError``. Documentation ------------- - General documentation freshening which takes imperative configuration into account in more places and uses glossary references more liberally. - Remove explanation of changing the request type in a new request event subscriber, as other predicates are now usually an easier way to get this done. - Added "Thread Locals" narrative chapter to documentation, and added a API chapter documenting the ``repoze.bfg.threadlocals`` module. - Added a "Special Exceptions" section to the "Views" narrative documentation chapter explaining the effect of raising ``repoze.bfg.exceptions.NotFound`` and ``repoze.bfg.exceptions.Forbidden`` from within view code. Dependencies ------------ - A new dependency on the ``twill`` package was added to the ``setup.py`` ``tests_require`` argument (Twill will only be downloaded when ``repoze.bfg`` ``setup.py test`` or ``setup.py nosetests`` is invoked). 1.2a4 (2009-12-07) ================== Features -------- - ``repoze.bfg.testing.DummyModel`` now accepts a new constructor keyword argument: ``__provides__``. If this constructor argument is provided, it should be an interface or a tuple of interfaces. The resulting model will then provide these interfaces (they will be attached to the constructed model via ``zope.interface.alsoProvides``). Bug Fixes --------- - Operation on GAE was broken, presumably because the ``repoze.bfg.configuration`` module began to attempt to import the ``repoze.bfg.chameleon_zpt`` and ``repoze.bfg.chameleon_text`` modules, and these cannot be used on non-CPython platforms. It now tolerates startup time import failures for these modules, and only raise an import error when a template from one of these packages is actually used. 1.2a3 (2009-12-02) ================== Bug Fixes --------- - The ``repoze.bfg.url.route_url`` function inappropriately passed along ``_query`` and/or ``_anchor`` arguments to the ``mapper.generate`` function, resulting in blowups. - When two views were registered with differering ``for`` interfaces or classes, and the ``for`` of first view registered was a superclass of the second, the ``repoze.bfg`` view machinery would incorrectly associate the two views with the same "multiview". Multiviews are meant to be collections of views that have *exactly* the same for/request/viewname values, without taking inheritance into account. Symptom: wrong view callable found even when you had correctly specified a ``for_`` interface/class during view configuration for one or both view configurations. Backwards Incompatibilities --------------------------- - The ``repoze.bfg.templating`` module has been removed; it had been deprecated in 1.1 and never actually had any APIs in it. 1.2a2 (2009-11-29) ================== Bug Fixes --------- - The long description of this package (as shown on PyPI) was not valid reStructuredText, and so was not renderable. - Trying to use an HTTP method name string such as ``GET`` as a ``request_type`` predicate argument caused a startup time failure when it was encountered in imperative configuration or in a decorator (symptom: ``Type Error: Required specification must be a specification``). This now works again, although ``request_method`` is now the preferred predicate argument for associating a view configuration with an HTTP request method. Documentation ------------- - Fixed "Startup" narrative documentation chapter; it was explaining "the old way" an application constructor worked. 1.2a1 (2009-11-28) ================== Features -------- - An imperative configuration mode. A ``repoze.bfg`` application can now begin its life as a single Python file. Later, the application might evolve into a set of Python files in a package. Even later, it might start making use of other configuration features, such as ``ZCML``. But neither the use of a package nor the use of non-imperative configuration is required to create a simple ``repoze.bfg`` application any longer. Imperative configuration makes ``repoze.bfg`` competetive with "microframeworks" such as `Bottle `_ and `Tornado `_. ``repoze.bfg`` has a good deal of functionality that most microframeworks lack, so this is hopefully a "best of both worlds" feature. The simplest possible ``repoze.bfg`` application is now:: from webob import Response from wsgiref import simple_server from repoze.bfg.configuration import Configurator def hello_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() config.add_view(hello_world) app = config.make_wsgi_app() simple_server.make_server('', 8080, app).serve_forever() - A new class now exists: ``repoze.bfg.configuration.Configurator``. This class forms the basis for sharing machinery between "imperatively" configured applications and traditional declaratively-configured applications. - The ``repoze.bfg.testing.setUp`` function now accepts three extra optional keyword arguments: ``registry``, ``request`` and ``hook_zca``. If the ``registry`` argument is not ``None``, the argument will be treated as the registry that is set as the "current registry" (it will be returned by ``repoze.bfg.threadlocal.get_current_registry``) for the duration of the test. If the ``registry`` argument is ``None`` (the default), a new registry is created and used for the duration of the test. The value of the ``request`` argument is used as the "current request" (it will be returned by ``repoze.bfg.threadlocal.get_current_request``) for the duration of the test; it defaults to ``None``. If ``hook_zca`` is ``True`` (the default), the ``zope.component.getSiteManager`` function will be hooked with a function that returns the value of ``registry`` (or the default-created registry if ``registry`` is ``None``) instead of the registry returned by ``zope.component.getGlobalSiteManager``, causing the Zope Component Architecture API (``getSiteManager``, ``getAdapter``, ``getUtility``, and so on) to use the testing registry instead of the global ZCA registry. - The ``repoze.bfg.testing.tearDown`` function now accepts an ``unhook_zca`` argument. If this argument is ``True`` (the default), ``zope.component.getSiteManager.reset()`` will be called. This will cause the result of the ``zope.component.getSiteManager`` function to be the global ZCA registry (the result of ``zope.component.getGlobalSiteManager``) once again. - The ``run.py`` module in various ``repoze.bfg`` ``paster`` templates now use a ``repoze.bfg.configuration.Configurator`` class instead of the (now-legacy) ``repoze.bfg.router.make_app`` function to produce a WSGI application. Documentation ------------- - The documentation now uses the "request-only" view calling convention in most examples (as opposed to the ``context, request`` convention). This is a documentation-only change; the ``context, request`` convention is also supported and documented, and will be "forever". - ``repoze.bfg.configuration`` API documentation has been added. - A narrative documentation chapter entitled "Creating Your First ``repoze.bfg`` Application" has been added. This chapter details usage of the new ``repoze.bfg.configuration.Configurator`` class, and demonstrates a simplified "imperative-mode" configuration; doing ``repoze.bfg`` application configuration imperatively was previously much more difficult. - A narrative documentation chapter entitled "Configuration, Decorations and Code Scanning" explaining ZCML- vs. imperative- vs. decorator-based configuration equivalence. - The "ZCML Hooks" chapter has been renamed to "Hooks"; it documents how to override hooks now via imperative configuration and ZCML. - The explanation about how to supply an alternate "response factory" has been removed from the "Hooks" chapter. This feature may be removed in a later release (it still works now, it's just not documented). - Add a section entitled "Test Set Up and Tear Down" to the unittesting chapter. Bug Fixes ---------- - The ACL authorization policy debugging output when ``debug_authorization`` console debugging output was turned on wasn't as clear as it could have been when a view execution was denied due to an authorization failure resulting from the set of principals passed never having matched any ACE in any ACL in the lineage. Now in this case, we report ```` as the ACE value and either the root ACL or ```` if no ACL was found. - When two views were registered with the same ``accept`` argument, but were otherwise registered with the same arguments, if a request entered the application which had an ``Accept`` header that accepted *either* of the media types defined by the set of views registered with predicates that otherwise matched, a more or less "random" one view would "win". Now, we try harder to use the view callable associated with the view configuration that has the most specific ``accept`` argument. Thanks to Alberto Valverde for an initial patch. Internals --------- - The routes mapper is no longer a root factory wrapper. It is now consulted directly by the router. - The ``repoze.bfg.registry.make_registry`` callable has been removed. - The ``repoze.bfg.view.map_view`` callable has been removed. - The ``repoze.bfg.view.owrap_view`` callable has been removed. - The ``repoze.bfg.view.predicate_wrap`` callable has been removed. - The ``repoze.bfg.view.secure_view`` callable has been removed. - The ``repoze.bfg.view.authdebug_view`` callable has been removed. - The ``repoze.bfg.view.renderer_from_name`` callable has been removed. Use ``repoze.bfg.configuration.Configurator.renderer_from_name`` instead (still not an API, however). - The ``repoze.bfg.view.derive_view`` callable has been removed. Use ``repoze.bfg.configuration.Configurator.derive_view`` instead (still not an API, however). - The ``repoze.bfg.settings.get_options`` callable has been removed. Its job has been subsumed by the ``repoze.bfg.settings.Settings`` class constructor. - The ``repoze.bfg.view.requestonly`` function has been moved to ``repoze.bfg.configuration.requestonly``. - The ``repoze.bfg.view.rendered_response`` function has been moved to ``repoze.bfg.configuration.rendered_response``. - The ``repoze.bfg.view.decorate_view`` function has been moved to ``repoze.bfg.configuration.decorate_view``. - The ``repoze.bfg.view.MultiView`` class has been moved to ``repoze.bfg.configuration.MultiView``. - The ``repoze.bfg.zcml.Uncacheable`` class has been removed. - The ``repoze.bfg.resource.resource_spec`` function has been removed. - All ZCML directives which deal with attributes which are paths now use the ``path`` method of the ZCML context to resolve a relative name to an absolute one (imperative configuration requirement). - The ``repoze.bfg.scripting.get_root`` API now uses a 'real' WebOb request rather than a FakeRequest when it sets up the request as a threadlocal. - The ``repoze.bfg.traversal.traverse`` API now uses a 'real' WebOb request rather than a FakeRequest when it calls the traverser. - The ``repoze.bfg.request.FakeRequest`` class has been removed. - Most uses of the ZCA threadlocal API (the ``getSiteManager``, ``getUtility``, ``getAdapter``, ``getMultiAdapter`` threadlocal API) have been removed from the core. Instead, when a threadlocal is necessary, the core uses the ``repoze.bfg.threadlocal.get_current_registry`` API to obtain the registry. - The internal ILogger utility named ``repoze.bfg.debug`` is now just an IDebugLogger unnamed utility. A named utility with the old name is registered for b/w compat. - The ``repoze.bfg.interfaces.ITemplateRendererFactory`` interface was removed; it has become unused. - Instead of depending on the ``martian`` package to do code scanning, we now just use our own scanning routines. - We now no longer have a dependency on ``repoze.zcml`` package; instead, the ``repoze.bfg`` package includes implementations of the ``adapter``, ``subscriber`` and ``utility`` directives. - Relating to the following functions: ``repoze.bfg.view.render_view`` ``repoze.bfg.view.render_view_to_iterable`` ``repoze.bfg.view.render_view_to_response`` ``repoze.bfg.view.append_slash_notfound_view`` ``repoze.bfg.view.default_notfound_view`` ``repoze.bfg.view.default_forbidden_view`` ``repoze.bfg.configuration.rendered_response`` ``repoze.bfg.security.has_permission`` ``repoze.bfg.security.authenticated_userid`` ``repoze.bfg.security.effective_principals`` ``repoze.bfg.security.view_execution_permitted`` ``repoze.bfg.security.remember`` ``repoze.bfg.security.forget`` ``repoze.bfg.url.route_url`` ``repoze.bfg.url.model_url`` ``repoze.bfg.url.static_url`` ``repoze.bfg.traversal.virtual_root`` Each of these functions now expects to be called with a request object that has a ``registry`` attribute which represents the current ``repoze.bfg`` registry. They fall back to obtaining the registry from the threadlocal API. Backwards Incompatibilites -------------------------- - Unit tests which use ``zope.testing.cleanup.cleanUp`` for the purpose of isolating tests from one another may now begin to fail due to lack of isolation between tests. Here's why: In repoze.bfg 1.1 and prior, the registry returned by ``repoze.bfg.threadlocal.get_current_registry`` when no other registry had been pushed on to the threadlocal stack was the ``zope.component.globalregistry.base`` global registry (aka the result of ``zope.component.getGlobalSiteManager()``). In repoze.bfg 1.2+, however, the registry returned in this situation is the new module-scope ``repoze.bfg.registry.global_registry`` object. The ``zope.testing.cleanup.cleanUp`` function clears the ``zope.component.globalregistry.base`` global registry unconditionally. However, it does not know about the ``repoze.bfg.registry.global_registry`` object, so it does not clear it. If you use the ``zope.testing.cleanup.cleanUp`` function in the ``setUp`` of test cases in your unit test suite instead of using the (more correct as of 1.1) ``repoze.bfg.testing.setUp``, you will need to replace all calls to ``zope.testing.cleanup.cleanUp`` with a call to ``repoze.bfg.testing.setUp``. If replacing all calls to ``zope.testing.cleanup.cleanUp`` with a call to ``repoze.bfg.testing.setUp`` is infeasible, you can put this bit of code somewhere that is executed exactly **once** (*not* for each test in a test suite; in the `` __init__.py`` of your package or your package's ``tests`` subpackage would be a reasonable place):: import zope.testing.cleanup from repoze.bfg.testing import setUp zope.testing.cleanup.addCleanUp(setUp) - When there is no "current registry" in the ``repoze.bfg.threadlocal.manager`` threadlocal data structure (this is the case when there is no "current request" or we're not in the midst of a ``r.b.testing.setUp``-bounded unit test), the ``.get`` method of the manager returns a data structure containing a *global* registry. In previous releases, this function returned the global Zope "base" registry: the result of ``zope.component.getGlobalSiteManager``, which is an instance of the ``zope.component.registry.Component`` class. In this release, however, the global registry returns a globally importable instance of the ``repoze.bfg.registry.Registry`` class. This registry instance can always be imported as ``repoze.bfg.registry.global_registry``. Effectively, this means that when you call ``repoze.bfg.threadlocal.get_current_registry`` when no request or ``setUp`` bounded unit test is in effect, you will always get back the global registry that lives in ``repoze.bfg.registry.global_registry``. It also means that ``repoze.bfg`` APIs that *call* ``get_current_registry`` will use this registry. This change was made because ``repoze.bfg`` now expects the registry it uses to have a slightly different API than a bare instance of ``zope.component.registry.Components``. - View registration no longer registers a ``repoze.bfg.interfaces.IViewPermission`` adapter (it is no longer checked by the framework; since 1.1, views have been responsible for providing their own security). - The ``repoze.bfg.router.make_app`` callable no longer accepts the ``authentication_policy`` nor the ``authorization_policy`` arguments. This feature was deprecated in version 1.0 and has been removed. - Obscure: the machinery which configured views with a ``request_type`` *and* a ``route_name`` would ignore the request interface implied by ``route_name`` registering a view only for the interface implied by ``request_type``. In the unlikely event that you were trying to use these two features together, the symptom would have been that views that named a ``request_type`` but which were also associated with routes were not found when the route matched. Now if a view is configured with both a ``request_type`` and a ``route_name``, an error is raised. - The ``route`` ZCML directive now no longer accepts the ``request_type`` or ``view_request_type`` attributes. These attributes didn't actually work in any useful way (see entry above this one). - Because the ``repoze.bfg`` package now includes implementations of the ``adapter``, ``subscriber`` and ``utility`` ZCML directives, it is now an error to have ```` in the ZCML of a ``repoze.bfg`` application. A ZCML conflict error will be raised if your ZCML does so. This shouldn't be an issue for "normal" installations; it has always been the responsibility of the ``repoze.bfg.includes`` ZCML to include this file in the past; it now just doesn't. - The ``repoze.bfg.testing.zcml_configure`` API was removed. Use the ``Configurator.load_zcml`` API instead. Deprecations ------------ - The ``repoze.bfg.router.make_app`` function is now nominally deprecated. Its import and usage does not throw a warning, nor will it probably ever disappear. However, using a ``repoze.bfg.configuration.Configurator`` class is now the preferred way to generate a WSGI application. Note that ``make_app`` calls ``zope.component.getSiteManager.sethook( repoze.bfg.threadlocal.get_current_registry)`` on the caller's behalf, hooking ZCA global API lookups, for backwards compatibility purposes. If you disuse ``make_app``, your calling code will need to perform this call itself, at least if your application uses the ZCA global API (``getSiteManager``, ``getAdapter``, etc). Dependencies ------------ - A dependency on the ``martian`` package has been removed (its functionality is replaced internally). - A dependency on the ``repoze.zcml`` package has been removed (its functionality is replaced internally). 1.1.1 (2009-11-21) ================== Bug Fixes --------- - "Hybrid mode" applications (applications which explicitly used traversal *after* url dispatch via ```` paths containing the ``*traverse`` element) were broken in 1.1-final and all 1.1 alpha and beta releases. Views registered without a ``route_name`` route shadowed views registered with a ``route_name`` inappropriately. 1.1 (2009-11-15) ================ Internals --------- - Remove dead IRouteRequirement interface from ``repoze.bfg.zcml`` module. Documentation ------------- - Improve the "Extending an Existing Application" narrative chapter. - Add more sections to the "Defending Design" chapter. 1.1b4 (2009-11-12) ================== Bug Fixes --------- - Use ``alsoProvides`` in the urldispatch module to attach an interface to the request rather than ``directlyProvides`` to avoid disturbing interfaces set in a NewRequest event handler. Documentation ------------- - Move 1.0.1 and previous changelog to HISTORY.txt. - Add examples to ``repoze.bfg.url.model_url`` docstring. - Add "Defending BFG Design" chapter to frontpage docs. Templates --------- - Remove ``ez_setup.py`` and its import from all paster templates, samples, and tutorials for ``distribute`` compatibility. The documentation already explains how to install virtualenv (which will include some ``setuptools`` package), so these files, imports and usages were superfluous. Deprecations ------------ - The ``options`` kw arg to the ``repoze.bfg.router.make_app`` function is deprecated. In its place is the keyword argument ``settings``. The ``options`` keyword continues to work, and a deprecation warning is not emitted when it is detected. However, the paster templates, code samples, and documentation now make reference to ``settings`` rather than ``options``. This change/deprecation was mainly made for purposes of clarity and symmetry with the ``get_settings()`` API and dicussions of "settings" in various places in the docs: we want to use the same name to refer to the same thing everywhere. 1.1b3 (2009-11-06) ================== Features -------- - ``repoze.bfg.testing.registerRoutesMapper`` testing facility added. This testing function registers a routes "mapper" object in the registry, for tests which require its presence. This function is documented in the ``repoze.bfg.testing`` API documentation. Bug Fixes --------- - Compound statements that used an assignment entered into in an interactive IPython session invoked via ``paster bfgshell`` no longer fail to mutate the shell namespace correctly. For example, this set of statements used to fail:: In [2]: def bar(x): return x ...: In [3]: list(bar(x) for x in 'abc') Out[3]: NameError: 'bar' In this release, the ``bar`` function is found and the correct output is now sent to the console. Thanks to Daniel Holth for the patch. - The ``bfgshell`` command did not function properly; it was still expecting to be able to call the root factory with a bare ``environ`` rather than a request object. Backwards Incompatibilities --------------------------- - The ``repoze.bfg.scripting.get_root`` function now expects a ``request`` object as its second argument rather than an ``environ``. 1.1b2 (2009-11-02) ================== Bug Fixes --------- - Prevent PyPI installation failure due to ``easy_install`` trying way too hard to guess the best version of Paste. When ``easy_install`` pulls from PyPI it reads links off various pages to determine "more up to date" versions. It incorrectly picks up a link for an ancient version of a package named "Paste-Deploy-0.1" (note the dash) when trying to find the "Paste" distribution and somehow believes it's the latest version of "Paste". It also somehow "helpfully" decides to check out a version of this package from SVN. We pin the Paste dependency version to a version greater than 1.7 to work around this ``easy_install`` bug. Documentation ------------- - Fix "Hybrid" narrative chapter: stop claiming that ```` statements that mention a route_name need to come afer (in XML order) the ```` statement which creates the route. This hasn't been true since 1.1a1. - "What's New in ``repoze.bfg`` 1.1" document added to narrative documentation. Features -------- - Add a new event type: ``repoze.bfg.events.AfterTraversal``. Events of this type will be sent after traversal is completed, but before any view code is invoked. Like ``repoze.bfg.events.NewRequest``, This event will have a single attribute: ``request`` representing the current request. Unlike the request attribute of ``repoze.bfg.events.NewRequest`` however, during an AfterTraversal event, the request object will possess attributes set by the traverser, most notably ``context``, which will be the context used when a view is found and invoked. The interface ``repoze.bfg.events.IAfterTraversal`` can be used to subscribe to the event. For example:: Like any framework event, a subscriber function should expect one parameter: ``event``. Dependencies ------------ - Rather than depending on ``chameleon.core`` and ``chameleon.zpt`` distributions individually, depend on Malthe's repackaged ``Chameleon`` distribution (which includes both ``chameleon.core`` and ``chameleon.zpt``). 1.1b1 (2009-11-01) ================== Bug Fixes --------- - The routes root factory called route factories and the default route factory with an environ rather than a request. One of the symptoms of this bug: applications generated using the ``bfg_zodb`` paster template in 1.1a9 did not work properly. - Reinstate ``renderer`` alias for ``view_renderer`` in the ```` ZCML directive (in-the-wild 1.1a bw compat). - ``bfg_routesalchemy`` paster template: change ```` declarations: rename ``renderer`` attribute to ``view_renderer``. - Header values returned by the ``authtktauthenticationpolicy`` ``remember`` and ``forget`` methods would be of type ``unicode``. This violated the WSGI spec, causing a ``TypeError`` to be raised when these headers were used under ``mod_wsgi``. - If a BFG app that had a route matching the root URL was mounted under a path in modwsgi, ala ``WSGIScriptAlias /myapp /Users/chrism/projects/modwsgi/env/bfg.wsgi``, the home route (a route with the path of ``'/'`` or ``''``) would not match when the path ``/myapp`` was visited (only when the path ``/myapp/`` was visited). This is now fixed: if the urldispatch root factory notes that the PATH_INFO is empty, it converts it to a single slash before trying to do matching. Documentation ------------- - In ```` declarations in tutorial ZCML, rename ``renderer`` attribute to ``view_renderer`` (fwd compat). - Fix various tutorials broken by 1.1a9 ```` directive changes. Internal -------- - Deal with a potential circref in the traversal module. 1.1a9 (2009-10-31) ================== Bug Fixes --------- - An incorrect ZCML conflict would be encountered when the ``request_param`` predicate attribute was used on the ZCML ``view`` directive if any two otherwise same-predicated views had the combination of a predicate value with an ``=`` sign and one without (e.g. ``a`` vs. ``a=123``). Features -------- - In previous versions of BFG, the "root factory" (the ``get_root`` callable passed to ``make_app`` or a function pointed to by the ``factory`` attribute of a route) was called with a "bare" WSGI environment. In this version, and going forward, it will be called with a ``request`` object. The request object passed to the factory implements dictionary-like methods in such a way that existing root factory code which expects to be passed an environ will continue to work. - The ``__call__`` of a plugin "traverser" implementation (registered as an adapter for ``ITraverser`` or ``ITraverserFactory``) will now receive a *request* as the single argument to its ``__call__`` method. In previous versions it was passed a WSGI ``environ`` object. The request object passed to the factory implements dictionary-like methods in such a way that existing traverser code which expects to be passed an environ will continue to work. - The ZCML ``route`` directive's attributes ``xhr``, ``request_method``, ``path_info``, ``request_param``, ``header`` and ``accept`` are now *route* predicates rather than *view* predicates. If one or more of these predicates is specified in the route configuration, all of the predicates must return true for the route to match a request. If one or more of the route predicates associated with a route returns ``False`` when checked during a request, the route match fails, and the next match in the routelist is tried. This differs from the previous behavior, where no route predicates existed and all predicates were considered view predicates, because in that scenario, the next route was not tried. Documentation ------------- - Various changes were made to narrative and API documentation supporting the change from passing a request rather than an environ to root factories and traversers. Internal -------- - The request implements dictionary-like methods that mutate and query the WSGI environ. This is only for the purpose of backwards compatibility with root factories which expect an ``environ`` rather than a request. - The ``repoze.bfg.request.create_route_request_factory`` function, which returned a request factory was removed in favor of a ``repoze.bfg.request.route_request_interface`` function, which returns an interface. - The ``repoze.bfg.request.Request`` class, which is a subclass of ``webob.Request`` now defines its own ``__setattr__``, ``__getattr__`` and ``__delattr__`` methods, which override the default WebOb behavior. The default WebOb behavior stores attributes of the request in ``self.environ['webob.adhoc_attrs']``, and retrieves them from that dictionary during a ``__getattr__``. This behavior was undesirable for speed and "expectation" reasons. Now attributes of the ``request`` are stored in ``request.__dict__`` (as you otherwise might expect from an object that did not override these methods). - The router no longer calls ``repoze.bfg.traversal._traverse`` and does its work "inline" (speed). - Reverse the order in which the router calls the request factory and the root factory. The request factory is now called first; the resulting request is passed to the root factory. - The ``repoze.bfg.request.request_factory`` function has been removed. Its functionality is no longer required. - The "routes root factory" that wraps the default root factory when there are routes mentioned in the configuration now attaches an interface to the request via ``zope.interface.directlyProvides``. This replaces logic in the (now-gone) ``repoze.bfg.request.request_factory`` function. - The ``route`` and ``view`` ZCML directives now register an interface as a named utility (retrieved from ``repoze.bfg.request.route_request_interface``) rather than a request factory (the previous return value of the now-missing ``repoze.bfg.request.create_route_request_factory``. - The ``repoze.bfg.functional`` module was renamed to ``repoze.bfg.compat``. Backwards Incompatibilities --------------------------- - Explicitly revert the feature introduced in 1.1a8: where the name ``root`` is available as an attribute of the request before a NewRequest event is emitted. This makes some potential future features impossible, or at least awkward (such as grouping traversal and view lookup into a single adapter lookup). - The ``containment``, ``attr`` and ``renderer`` attributes of the ``route`` ZCML directive were removed. 1.1a8 (2009-10-27) ================== Features -------- - Add ``path_info`` view configuration predicate. - ``paster bfgshell`` now supports IPython if it's available for import. Thanks to Daniel Holth for the initial patch. - Add ``repoze.bfg.testing.registerSettings`` API, which is documented in the "repoze.bfg.testing" API chapter. This allows for registration of "settings" values obtained via ``repoze.bfg.settings.get_settings()`` for use in unit tests. - The name ``root`` is available as an attribute of the request slightly earlier now (before a NewRequest event is emitted). ``root`` is the result of the application "root factory". - Added ``max_age`` parameter to ``authtktauthenticationpolicy`` ZCML directive. If this value is set, it must be an integer representing the number of seconds which the auth tkt cookie will survive. Mainly, its existence allows the auth_tkt cookie to survive across browser sessions. Bug Fixes --------- - Fix bug encountered during "scan" (when ```` directive is used in ZCML) introduced in 1.1a7. Symptom: ``AttributeError: object has no attribute __provides__`` raised at startup time. - The ``reissue_time`` argument to the ``authtktauthenticationpolicy`` ZCML directive now actually works. When it is set to an integer value, an authticket set-cookie header is appended to the response whenever a request requires authentication and 'now' minus the authticket's timestamp is greater than ``reissue_time`` seconds. Documentation ------------- - Add a chapter titled "Request and Response" to the narrative documentation, content cribbed from the WebOb documentation. - Call out predicate attributes of ZCML directive within "Views" chapter. - Fix route_url documentation (``_query`` argument documented as ``query`` and ``_anchor`` argument documented as ``anchor``). Backwards Incompatibilities --------------------------- - The ``authtkt`` authentication policy ``remember`` method now no longer honors ``token`` or ``userdata`` keyword arguments. Internal -------- - Change how ``bfg_view`` decorator works when used as a class method decorator. In 1.1a7, the``scan``directive actually tried to grope every class in scanned package at startup time, calling ``dir`` against each found class, and subsequently invoking ``getattr`` against each thing found by ``dir`` to see if it was a method. This led to some strange symptoms (e.g. ``AttributeError: object has no attribute __provides__``), and was generally just a bad idea. Now, instead of groping classes for methods at startup time, we just cause the ``bfg_view`` decorator itself to populate the method's class' ``__dict__`` when it is used as a method decorator. This also requires a nasty _getframe thing but it's slightly less nasty than the startup time groping behavior. This is essentially a reversion back to 1.1a6 "grokking" behavior plus some special magic for using the ``bfg_view`` decorator as method decorator inside the ``bfg_view`` class itself. - The router now checks for a ``global_response_headers`` attribute of the request object before returning a response. If this value exists, it is presumed to be a sequence of two-tuples, representing a set of headers to append to the 'normal' response headers. This feature is internal, rather than exposed externally, because it's unclear whether it will stay around in the long term. It was added to support the ``reissue_time`` feature of the authtkt authentication policy. - The interface ITraverserFactory is now just an alias for ITraverser. 1.1a7 (2009-10-18) ================== Features -------- - More than one ``@bfg_view`` decorator may now be stacked on top of any number of others. Each invocation of the decorator registers a single view configuration. For instance, the following combination of decorators and a function will register two view configurations for the same view callable:: from repoze.bfg.view import bfg_view @bfg_view(name='edit') @bfg_view(name='change') def edit(context, request): pass This makes it possible to associate more than one view configuration with a single callable without requiring any ZCML. - The ``@bfg_view`` decorator can now be used against a class method:: from webob import Response from repoze.bfg.view import bfg_view class MyView(object): def __init__(self, context, request): self.context = context self.request = request @bfg_view(name='hello') def amethod(self): return Response('hello from %s!' % self.context) When the bfg_view decorator is used against a class method, a view is registered for the *class* (it's a "class view" where the "attr" happens to be the name of the method it is attached to), so the class it's defined within must have a suitable constructor: one that accepts ``context, request`` or just ``request``. Documentation ------------- - Added ``Changing the Traverser`` and ``Changing How :mod:`repoze.bfg.url.model_url` Generates a URL`` to the "Hooks" narrative chapter of the docs. Internal -------- - Remove ``ez_setup.py`` and imports of it within ``setup.py``. In the new world, and as per virtualenv setup instructions, people will already have either setuptools or distribute. 1.1a6 (2009-10-15) ================== Features -------- - Add ``xhr``, ``accept``, and ``header`` view configuration predicates to ZCML view declaration, ZCML route declaration, and ``bfg_view`` decorator. See the ``Views`` narrative documentation chapter for more information about these predicates. - Add ``setUp`` and ``tearDown`` functions to the ``repoze.bfg.testing`` module. Using ``setUp`` in a test setup and ``tearDown`` in a test teardown is now the recommended way to do component registry setup and teardown. Previously, it was recommended that a single function named ``repoze.bfg.testing.cleanUp`` be called in both the test setup and tear down. ``repoze.bfg.testing.cleanUp`` still exists (and will exist "forever" due to its widespread use); it is now just an alias for ``repoze.bfg.testing.setUp`` and is nominally deprecated. - The BFG component registry is now available in view and event subscriber code as an attribute of the request ie. ``request.registry``. This fact is currently undocumented except for this note, because BFG developers never need to interact with the registry directly anywhere else. - The BFG component registry now inherits from ``dict``, meaning that it can optionally be used as a simple dictionary. *Component* registrations performed against it via e.g. ``registerUtility``, ``registerAdapter``, and similar API methods are kept in a completely separate namespace than its dict members, so using the its component API methods won't effect the keys and values in the dictionary namespace. Likewise, though the component registry "happens to be" a dictionary, use of mutating dictionary methods such as ``__setitem__`` will have no influence on any component registrations made against it. In other words, the registry object you obtain via e.g. ``repoze.bfg.threadlocal.get_current_registry`` or ``request.registry`` happens to be both a component registry and a dictionary, but using its component-registry API won't impact data added to it via its dictionary API and vice versa. This is a forward compatibility move based on the goals of "marco". - Expose and document ``repoze.bfg.testing.zcml_configure`` API. This function populates a component registry from a ZCML file for testing purposes. It is documented in the "Unit and Integration Testing" chapter. Documentation ------------- - Virtual hosting narrative docs chapter updated with info about ``mod_wsgi``. - Point all index URLs at the literal 1.1 index (this alpha cycle may go on a while). - Various tutorial test modules updated to use ``repoze.bfg.testing.setUp`` and ``repoze.bfg.testing.tearDown`` methods in order to encourage this as best practice going forward. - Added "Creating Integration Tests" section to unit testing narrative documentation chapter. As a result, the name of the unittesting chapter is now "Unit and Integration Testing". Backwards Incompatibilities --------------------------- - Importing ``getSiteManager`` and ``get_registry`` from ``repoze.bfg.registry`` is no longer supported. These imports were deprecated in repoze.bfg 1.0. Import of ``getSiteManager`` should be done as ``from zope.component import getSiteManager``. Import of ``get_registry`` should be done as ``from repoze.bfg.threadlocal import get_current_registry``. This was done to prevent a circular import dependency. - Code bases which alternately invoke both ``zope.testing.cleanup.cleanUp`` and ``repoze.bfg.testing.cleanUp`` (treating them equivalently, using them interchangeably) in the setUp/tearDown of unit tests will begin to experience test failures due to lack of test isolation. The "right" mechanism is ``repoze.bfg.testing.cleanUp`` (or the combination of ``repoze.bfg.testing.setUp`` and ``repoze.bfg.testing.tearDown``). but a good number of legacy codebases will use ``zope.testing.cleanup.cleanUp`` instead. We support ``zope.testing.cleanup.cleanUp`` but not in combination with ``repoze.bfg.testing.cleanUp`` in the same codebase. You should use one or the other test cleanup function in a single codebase, but not both. Internal -------- - Created new ``repoze.bfg.configuration`` module which assumes responsibilities previously held by the ``repoze.bfg.registry`` and ``repoze.bfg.router`` modules (avoid a circular import dependency). - The result of the ``zope.component.getSiteManager`` function in unit tests set up with ``repoze.bfg.testing.cleanUp`` or ``repoze.bfg.testing.setUp`` will be an instance of ``repoze.bfg.registry.Registry`` instead of the global ``zope.component.globalregistry.base`` registry. This also means that the threadlocal ZCA API functions such as ``getAdapter`` and ``getUtility`` as well as internal BFG machinery (such as ``model_url`` and ``route_url``) will consult this registry within unit tests. This is a forward compatibility move based on the goals of "marco". - Removed ``repoze.bfg.testing.addCleanUp`` function and associated module-scope globals. This was never an API. 1.1a5 (2009-10-10) ================== Documentation ------------- - Change "Traversal + ZODB" and "URL Dispatch + SQLAlchemy" Wiki tutorials to make use of the new-to-1.1 "renderer" feature (return dictionaries from all views). - Add tests to the "URL Dispatch + SQLAlchemy" tutorial after the "view" step. - Added a diagram of model graph traversal to the "Traversal" narrative chapter of the documentation. - An ``exceptions`` API chapter was added, documenting the new ``repoze.bfg.exceptions`` module. - Describe "request-only" view calling conventions inside the urldispatch narrative chapter, where it's most helpful. - Add a diagram which explains the operation of the BFG router to the "Router" narrative chapter. Features -------- - Add a new ``repoze.bfg.testing`` API: ``registerRoute``, for registering routes to satisfy calls to e.g. ``repoze.bfg.url.route_url`` in unit tests. - The ``notfound`` and ``forbidden`` ZCML directives now accept the following addtional attributes: ``attr``, ``renderer``, and ``wrapper``. These have the same meaning as they do in the context of a ZCML ``view`` directive. - For behavior like Django's ``APPEND_SLASH=True``, use the ``repoze.bfg.view.append_slash_notfound_view`` view as the Not Found view in your application. When this view is the Not Found view (indicating that no view was found), and any routes have been defined in the configuration of your application, if the value of ``PATH_INFO`` does not already end in a slash, and if the value of ``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP redirect to the slash-appended PATH_INFO. Note that this will *lose* ``POST`` data information (turning it into a GET), so you shouldn't rely on this to redirect POST requests. - Speed up ``repoze.bfg.location.lineage`` slightly. - Speed up ``repoze.bfg.encode.urlencode`` (nee' ``repoze.bfg.url.urlencode``) slightly. - Speed up ``repoze.bfg.traversal.model_path``. - Speed up ``repoze.bfg.traversal.model_path_tuple`` slightly. - Speed up ``repoze.bfg.traversal.traverse`` slightly. - Speed up ``repoze.bfg.url.model_url`` slightly. - Speed up ``repoze.bfg.url.route_url`` slightly. - Sped up ``repoze.bfg.traversal.ModelGraphTraverser:__call__`` slightly. - Minor speedup of ``repoze.bfg.router.Router.__call__``. - New ``repoze.bfg.exceptions`` module was created to house exceptions that were previously sprinkled through various modules. Internal -------- - Move ``repoze.bfg.traversal._url_quote`` into ``repoze.bfg.encode`` as ``url_quote``. Deprecations ------------ - The import of ``repoze.bfg.view.NotFound`` is deprecated in favor of ``repoze.bfg.exceptions.NotFound``. The old location still functions, but emits a deprecation warning. - The import of ``repoze.bfg.security.Unauthorized`` is deprecated in favor of ``repoze.bfg.exceptions.Forbidden``. The old location still functions but emits a deprecation warning. The rename from ``Unauthorized`` to ``Forbidden`` brings parity to the name of the exception and the system view it invokes when raised. Backwards Incompatibilities --------------------------- - We previously had a Unicode-aware wrapper for the ``urllib.urlencode`` function named ``repoze.bfg.url.urlencode`` which delegated to the stdlib function, but which marshalled all unicode values to utf-8 strings before calling the stdlib version. A newer replacement now lives in ``repoze.bfg.encode`` The replacement does not delegate to the stdlib. The replacement diverges from the stdlib implementation and the previous ``repoze.bfg.url`` url implementation inasmuch as its ``doseq`` argument is now a decoy: it always behaves in the ``doseq=True`` way (which is the only sane behavior) for speed purposes. The old import location (``repoze.bfg.url.urlencode``) still functions and has not been deprecated. - In 0.8a7, the return value expected from an object implementing ``ITraverserFactory`` was changed from a sequence of values to a dictionary containing the keys ``context``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, ``virtual_root_path``, and ``root``. Until now, old-style traversers which returned a sequence have continued to work but have generated a deprecation warning. In this release, traversers which return a sequence instead of a dictionary will no longer work. 1.1a4 (2009-09-23) ================== Bug Fixes --------- - On 64-bit Linux systems, views that were members of a multiview (orderings of views with predicates) were not evaluated in the proper order. Symptom: in a configuration that had two views with the same name but one with a ``request_method=POST`` predicate and one without, the one without the predicate would be called unconditionally (even if the request was a POST request). Thanks much to Sebastien Douche for providing the buildbots that pointed this out. Documentation ------------- - Added a tutorial which explains how to use ``repoze.session`` (ZODB-based sessions) in a ZODB-based repoze.bfg app. - Added a tutorial which explains how to add ZEO to a ZODB-based ``repoze.bfg`` application. - Added a tutorial which explains how to run a ``repoze.bfg`` application under `mod_wsgi `_. See "Running a repoze.bfg Application under mod_wsgi" in the tutorials section of the documentation. Features -------- - Add a ``repoze.bfg.url.static_url`` API which is capable of generating URLs to static resources defined by the ```` ZCML directive. See the "Views" narrative chapter's section titled "Generating Static Resource URLs" for more information. - Add a ``string`` renderer. This renderer converts a non-Response return value of any view callble into a string. It is documented in the "Views" narrative chapter. - Give the ``route`` ZCML directive the ``view_attr`` and ``view_renderer`` parameters (bring up to speed with 1.1a3 features). These can also be spelled as ``attr`` and ``renderer``. Backwards Incompatibilities --------------------------- - An object implementing the ``IRenderer`` interface (and ``ITemplateRenderer`, which is a subclass of ``IRenderer``) must now accept an extra ``system`` argument in its ``__call__`` method implementation. Values computed by the system (as opposed to by the view) are passed by the system in the ``system`` parameter, which will always be a dictionary. Keys in the dictionary include: ``view`` (the view object that returned the value), ``renderer_name`` (the template name or simple name of the renderer), ``context`` (the context object passed to the view), and ``request`` (the request object passed to the view). Previously only ITemplateRenderers received system arguments as elements inside the main ``value`` dictionary. Internal -------- - The way ``bfg_view`` declarations are scanned for has been modified. This should have no external effects. - Speed: do not register an ITraverserFactory in configure.zcml; instead rely on queryAdapter and a manual default to ModelGraphTraverser. - Speed: do not register an IContextURL in configure.zcml; instead rely on queryAdapter and a manual default to TraversalContextURL. - General speed microimprovements for helloworld benchmark: replace try/excepts with statements which use 'in' keyword. 1.1a3 (2009-09-16) ================== Documentation ------------- - The "Views" narrative chapter in the documentation has been updated extensively to discuss "renderers". Features -------- - A ``renderer`` attribute has been added to view configurations, replacing the previous (1.1a2) version's ``template`` attribute. A "renderer" is an object which accepts the return value of a view and converts it to a string. This includes, but is not limited to, templating systems. - A new interface named ``IRenderer`` was added. The existing interface, ``ITemplateRenderer`` now derives from this new interface. This interface is internal. - A new interface named ``IRendererFactory`` was added. An existing interface named ``ITemplateRendererFactory`` now derives from this interface. This interface is internal. - The ``view`` attribute of the ``view`` ZCML directive is no longer required if the ZCML directive also has a ``renderer`` attribute. This is useful when the renderer is a template renderer and no names need be passed to the template at render time. - A new zcml directive ``renderer`` has been added. It is documented in the "Views" narrative chapter of the documentation. - A ZCML ``view`` directive (and the associated ``bfg_view`` decorator) can now accept a "wrapper" value. If a "wrapper" value is supplied, it is the value of a separate view's *name* attribute. When a view with a ``wrapper`` attribute is rendered, the "inner" view is first rendered normally. Its body is then attached to the request as "wrapped_body", and then a wrapper view name is looked up and rendered (using ``repoze.bfg.render_view_to_response``), passed the request and the context. The wrapper view is assumed to do something sensible with ``request.wrapped_body``, usually inserting its structure into some other rendered template. This feature makes it possible to specify (potentially nested) "owrap" relationships between views using only ZCML or decorators (as opposed always using ZPT METAL and analogues to wrap view renderings in outer wrappers). Dependencies ------------ - When used under Python < 2.6, BFG now has an installation time dependency on the ``simplejson`` package. Deprecations ------------ - The ``repoze.bfg.testing.registerDummyRenderer`` API has been deprecated in favor of ``repoze.bfg.testing.registerTemplateRenderer``. A deprecation warning is *not* issued at import time for the former name; it will exist "forever"; its existence has been removed from the documentation, however. - The ``repoze.bfg.templating.renderer_from_cache`` function has been moved to ``repoze.bfg.renderer.template_renderer_factory``. This was never an API, but code in the wild was spotted that used it. A deprecation warning is issued at import time for the former. Backwards Incompatibilities --------------------------- - The ``ITemplateRenderer`` interface has been changed. Previously its ``__call__`` method accepted ``**kw``. It now accepts a single positional parameter named ``kw`` (REVISED: it accepts two positional parameters as of 1.1a4: ``value`` and ``system``). This is mostly an internal change, but it was exposed in APIs in one place: if you've used the ``repoze.bfg.testing.registerDummyRenderer`` API in your tests with a custom "renderer" argument with your own renderer implementation, you will need to change that renderer implementation to accept ``kw`` instead of ``**kw`` in its ``__call__`` method (REVISED: make it accept ``value`` and ``system`` positional arguments as of 1.1a4). - The ``ITemplateRendererFactory`` interface has been changed. Previously its ``__call__`` method accepted an ``auto_reload`` keyword parameter. Now its ``__call__`` method accepts no keyword parameters. Renderers are now themselves responsible for determining details of auto-reload. This is purely an internal change. This interface was never external. - The ``template_renderer`` ZCML directive introduced in 1.1a2 has been removed. It has been replaced by the ``renderer`` directive. - The previous release (1.1a2) added a view configuration attribute named ``template``. In this release, the attribute has been renamed to ``renderer``. This signifies that the attribute is more generic: it can now be not just a template name but any renderer name (ala ``json``). - In the previous release (1.1a2), the Chameleon text template renderer was used if the system didn't associate the ``template`` view configuration value with a filename with a "known" extension. In this release, you must use a ``renderer`` attribute which is a path that ends with a ``.txt`` extension (e.g. ``templates/foo.txt``) to use the Chameleon text renderer. 1.1a2 (2009-09-14) ================== Features -------- - A ZCML ``view`` directive (and the associated ``bfg_view`` decorator) can now accept an "attr" value. If an "attr" value is supplied, it is considered a method named of the view object to be called when the response is required. This is typically only good for views that are classes or instances (not so useful for functions, as functions typically have no methods other than ``__call__``). - A ZCML ``view`` directive (and the associated ``bfg_view`` decorator) can now accept a "template" value. If a "template" value is supplied, and the view callable returns a dictionary, the associated template is rendered with the dictionary as keyword arguments. See the section named "Views That Have a ``template``" in the "Views" narrative documentation chapter for more information. 1.1a1 (2009-09-06) ================== Bug Fixes --------- - "tests" module removed from the bfg_alchemy paster template; these tests didn't work. - Bugfix: the ``discriminator`` for the ZCML "route" directive was incorrect. It was possible to register two routes that collided without the system spitting out a ConfigurationConflictError at startup time. Features -------- - Feature addition: view predicates. These are exposed as the ``request_method``, ``request_param``, and ``containment`` attributes of a ZCML ``view`` declaration, or the respective arguments to a ``@bfg_view`` decorator. View predicates can be used to register a view for a more precise set of environment parameters than was previously possible. For example, you can register two views with the same ``name`` with different ``request_param`` attributes. If the ``request.params`` dict contains 'foo' (request_param="foo"), one view might be called; if it contains 'bar' (request_param="bar"), another view might be called. ``request_param`` can also name a key/value pair ala ``foo=123``. This will match only when the ``foo`` key is in the request.params dict and it has the value '123'. This particular example makes it possible to write separate view functions for different form submissions. The other predicates, ``containment`` and ``request_method`` work similarly. ``containment`` is a view predicate that will match only when the context's graph lineage has an object possessing a particular class or interface, for example. ``request_method`` is a view predicate that will match when the HTTP ``REQUEST_METHOD`` equals some string (eg. 'POST'). - The ``@bfg_view`` decorator now accepts three additional arguments: ``request_method``, ``request_param``, and ``containment``. ``request_method`` is used when you'd like the view to match only a request with a particular HTTP ``REQUEST_METHOD``; a string naming the ``REQUEST_METHOD`` can also be supplied as ``request_type`` for backwards compatibility. ``request_param`` is used when you'd like a view to match only a request that contains a particular ``request.params`` key (with or without a value). ``containment`` is used when you'd like to match a request that has a context that has some class or interface in its graph lineage. These are collectively known as "view predicates". - The ``route`` ZCML directive now honors ``view_request_method``, ``view_request_param`` and ``view_containment`` attributes, which pass along these values to the associated view if any is provided. Additionally, the ``request_type`` attribute can now be spelled as ``view_request_type``, and ``permission`` can be spelled as ``view_permission``. Any attribute which starts with ``view_`` can now be spelled without the ``view_`` prefix, so ``view_for`` can be spelled as ``for`` now, etc. Both forms are documented in the urldispatch narraitve documentation chapter. - The ``request_param`` ZCML view directive attribute (and its ``bfg_view`` decorator cousin) can now specify both a key and a value. For example, ``request_param="foo=123"`` means that the foo key must have a value of ``123`` for the view to "match". - Allow ``repoze.bfg.traversal.find_interface`` API to use a class object as the argument to compare against the ``model`` passed in. This means you can now do ``find_interface(model, SomeClass)`` and the first object which is found in the lineage which has ``SomeClass`` as its class (or the first object found which has ``SomeClass`` as any of its superclasses) will be returned. - Added ``static`` ZCML directive which registers a route for a view that serves up files in a directory. See the "Views" narrative documentation chapter's "Serving Static Resources Using a ZCML Directive" section for more information. - The ``repoze.bfg.view.static`` class now accepts a string as its first argument ("root_dir") that represents a package-relative name e.g. ``somepackage:foo/bar/static``. This is now the preferred mechanism for spelling package-relative static paths using this class. A ``package_name`` keyword argument has been left around for backwards compatibility. If it is supplied, it will be honored. - The API ``repoze.bfg.testing.registerView`` now takes a ``permission`` argument. Use this instead of using ``repoze.bfg.testing.registerViewPermission``. - The ordering of route declarations vs. the ordering of view declarations that use a "route_name" in ZCML no longer matters. Previously it had been impossible to use a route_name from a route that had not yet been defined in ZCML (order-wise) within a "view" declaration. - The repoze.bfg router now catches both ``repoze.bfg.security.Unauthorized`` and ``repoze.bfg.view.NotFound`` exceptions while rendering a view. When the router catches an ``Unauthorized``, it returns the registered forbidden view. When the router catches a ``NotFound``, it returns the registered notfound view. Internal -------- - Change urldispatch internals: Route object is now constructed using a path, a name, and a factory instead of a name, a matcher, a generator, and a factory. - Move (non-API) default_view, default_forbidden_view, and default_notfound_view functions into the ``repoze.bfg.view`` module (moved from ``repoze.bfg.router``). - Removed ViewPermissionFactory from ``repoze.bfg.security``. View permission checking is now done by registering and looking up an ISecuredView. - The ``static`` ZCML directive now uses a custom root factory when constructing a route. - The interface ``IRequestFactories`` was removed from the repoze.bfg.interfaces module. This interface was never an API. - The function named ``named_request_factories`` and the data structure named ``DEFAULT_REQUEST_FACTORIES`` have been removed from the ``repoze.bfg.request`` module. These were never APIs. - The ``IViewPermissionFactory`` interface has been removed. This was never an API. Documentation ------------- - Request-only-convention examples in the "Views" narrative documentation were broken. - Fixed documentation bugs related to forget and remember in security API docs. - Fixed documentation for ``repoze.bfg.view.static`` (in narrative ``Views`` chapter). Deprecations ------------ - The API ``repoze.bfg.testing.registerViewPermission`` has been deprecated. Backwards Incompatibilities --------------------------- - The interfaces ``IPOSTRequest``, ``IGETRequest``, ``IPUTRequest``, ``IDELETERequest``, and ``IHEADRequest`` have been removed from the ``repoze.bfg.interfaces`` module. These were not documented as APIs post-1.0. Instead of using one of these, use a ``request_method`` ZCML attribute or ``request_method`` bfg_view decorator parameter containing an HTTP method name (one of ``GET``, ``POST``, ``HEAD``, ``PUT``, ``DELETE``) instead of one of these interfaces if you were using one explicitly. Passing a string in the set (``GET``, ``HEAD``, ``PUT``, ``POST``, ``DELETE``) as a ``request_type`` argument will work too. Rationale: instead of relying on interfaces attached to the request object, BFG now uses a "view predicate" to determine the request type. - Views registered without the help of the ZCML ``view`` directive are now responsible for performing their own authorization checking. - The ``registry_manager`` backwards compatibility alias importable from "repoze.bfg.registry", deprecated since repoze.bfg 0.9 has been removed. If you are tring to use the registry manager within a debug script of your own, use a combination of the "repoze.bfg.paster.get_app" and "repoze.bfg.scripting.get_root" APIs instead. - The ``INotFoundAppFactory`` interface has been removed; it has been deprecated since repoze.bfg 0.9. If you have something like the following in your ``configure.zcml``:: Replace it with something like:: See "Changing the Not Found View" in the "Hooks" chapter of the documentation for more information. - The ``IUnauthorizedAppFactory`` interface has been removed; it has been deprecated since repoze.bfg 0.9. If you have something like the following in your ``configure.zcml``:: Replace it with something like:: See "Changing the Forbidden View" in the "Hooks" chapter of the documentation for more information. - ``ISecurityPolicy``-based security policies, deprecated since repoze.bfg 0.9, have been removed. If you have something like this in your ``configure.zcml``, it will no longer work:: If ZCML like the above exists in your application, you will receive an error at startup time. Instead of the above, you'll need something like:: This is just an example. See the "Security" chapter of the repoze.bfg documentation for more information about configuring security policies. - Custom ZCML directives which register an authentication or authorization policy (ala "authtktauthenticationpolicy" or "aclauthorizationpolicy") should register the policy "eagerly" in the ZCML directive instead of from within a ZCML action. If an authentication or authorization policy is not found in the component registry by the view machinery during deferred ZCML processing, view security will not work as expected. 1.0.1 (2009-07-22) ================== - Added support for ``has_resource``, ``resource_isdir``, and ``resource_listdir`` to the resource "OverrideProvider"; this fixes a bug with a symptom that a file could not be overridden in a resource directory unless a file with the same name existed in the original directory being overridden. - Fixed documentation bug showing invalid test for values from the ``matchdict``: they are stored as attributes of the ``Article``, rather than subitems. - Fixed documentation bug showing wrong environment key for the ``matchdict`` produced by the matching route. - Added a workaround for a bug in Python 2.6, 2.6.1, and 2.6.2 having to do with a recursion error in the mimetypes module when trying to serve static files from Paste's FileApp: http://bugs.python.org/issue5853. Symptom: File "/usr/lib/python2.6/mimetypes.py", line 244, in guess_type return guess_type(url, strict) RuntimeError: maximum recursion depth exceeded. Thanks to Armin Ronacher for identifying the symptom and pointing out a fix. - Minor edits to tutorials for accuracy based on feedback. - Declared Paste and PasteDeploy dependencies. 1.0 (2009-07-05) ================ - Retested and added some content to GAE tutorial. - Edited "Extending" narrative docs chapter. - Added "Deleting the Database" section to the "Defining Models" chapter of the traversal wiki tutorial. - Spell checking of narratives and tutorials. 1.0b2 (2009-07-03) ================== - ``remoteuserauthenticationpolicy`` ZCML directive didn't work without an ``environ_key`` directive (didn't match docs). - Fix ``configure_zcml`` filespec check on Windows. Previously if an absolute filesystem path including a drive letter was passed as ``filename`` (or as ``configure_zcml`` in the options dict) to ``repoze.bfg.router.make_app``, it would be treated as a package:resource_name specification. - Fix inaccuracies and import errors in bfgwiki (traversal+ZODB) and bfgwiki2 (urldispatch+SA) tutorials. - Use bfgsite index for all tutorial setup.cfg files. - Full documentation grammar/style/spelling audit. 1.0b1 (2009-07-02) ================== Features -------- - Allow a Paste config file (``configure_zcml``) value or an environment variable (``BFG_CONFIGURE_ZCML``) to name a ZCML file (optionally package-relative) that will be used to bootstrap the application. Previously, the integrator could not influence which ZCML file was used to do the boostrapping (only the original application developer could do so). Documentation ------------- - Added a "Resources" chapter to the narrative documentation which explains how to override resources within one package from another package. - Added an "Extending" chapter to the narrative documentation which explains how to extend or modify an existing BFG application using another Python package and ZCML. 1.0a9 (2009-07-01) ================== Features -------- - Make it possible to pass strings in the form "package_name:relative/path" to APIs like ``render_template``, ``render_template_to_response``, and ``get_template``. Sometimes the package in which a caller lives is a direct namespace package, so the module which is returned is semi-useless for navigating from. In this way, the caller can control the horizontal and vertical of where things get looked up from. 1.0a8 (2009-07-01) ================== Deprecations ------------ - Deprecate the ``authentication_policy`` and ``authorization_policy`` arguments to ``repoze.bfg.router.make_app``. Instead, developers should use the various authentication policy ZCML directives (``repozewho1authenticationpolicy``, ``remoteuserauthenticationpolicy`` and ``authtktauthenticationpolicy``) and the `aclauthorizationpolicy`` authorization policy directive as described in the changes to the "Security" narrative documenation chapter and the wiki tutorials. Features -------- - Add three new ZCML directives which configure authentication policies: - ``repozewho1authenticationpolicy`` - ``remoteuserauthenticationpolicy`` - ``authtktauthenticationpolicy`` - Add a new ZCML directive which configures an ACL authorization policy named ``aclauthorizationpolicy``. Bug Fixes --------- - Bug fix: when a ``repoze.bfg.resource.PackageOverrides`` class was instantiated, and the package it was overriding already had a ``__loader__`` attribute, it would fail at startup time, even if the ``__loader__`` attribute was another PackageOverrides instance. We now replace any ``__loader__`` that is also a PackageOverrides instance. Symptom: ``ConfigurationExecutionError: : Package already has a __loader__ (probably a module in a zipped egg)``. 1.0a7 (2009-06-30) ================== Features -------- - Add a ``reload_resources`` configuration file setting (aka the ``BFG_RELOAD_RESOURCES`` environment variable). When this is set to true, the server never needs to be restarted when moving files between directory resource overrides (esp. for templates currently). - Add a ``reload_all`` configuration file setting (aka the ``BFG_RELOAD_ALL`` environment variable) that implies both ``reload_resources`` and ``reload_templates``. - The ``static`` helper view class now uses a ``PackageURLParser`` in order to allow for the overriding of static resources (CSS / logo files, etc) using the ``resource`` ZCML directive. The ``PackageURLParser`` class was added to a (new) ``static`` module in BFG; it is a subclass of the ``StaticURLParser`` class in ``paste.urlparser``. - The ``repoze.bfg.templating.renderer_from_cache`` function now checks for the ``reload_resources`` setting; if it's true, it does not register a template renderer (it won't use the registry as a template renderer cache). Documentation ------------- - Add ``pkg_resources`` to the glossary. - Update the "Environment" docs to note the existence of ``reload_resources`` and ``reload_all``. - Updated the ``bfg_alchemy`` paster template to include two views: the view on the root shows a list of links to records; the view on a record shows the details for that object. Internal -------- - Use a colon instead of a tab as the separator between package name and relpath to form the "spec" when register a ITemplateRenderer. - Register a ``repoze.bfg.resource.OverrideProvider`` as a pkg_resources provider only for modules which are known to have overrides, instead of globally, when a directive is used (performance). 1.0a6 (2009-06-29) ================== Bug Fixes --------- - Use ``caller_package`` function instead of ``caller_module`` function within ``templating`` to avoid needing to name the caller module in resource overrides (actually match docs). - Make it possible to override templates stored directly in a module with templates in a subdirectory of the same module, stored directly within another module, or stored in a subdirectory of another module (actually match docs). 1.0a5 (2009-06-28) ================== Features -------- - A new ZCML directive exists named "resource". This ZCML directive allows you to override Chameleon templates within a package (both directories full of templates and individual template files) with other templates in the same package or within another package. This allows you to "fake out" a view's use of a template, causing it to retrieve a different template than the one actually named by a relative path to a call like ``render_template_to_response('templates/mytemplate.pt')``. For example, you can override a template file by doing:: The string passed to "to_override" and "override_with" is named a "specification". The colon separator in a specification separates the package name from a package-relative directory name. The colon and the following relative path are optional. If they are not specified, the override attempts to resolve every lookup into a package from the directory of another package. For example:: Individual subdirectories within a package can also be overridden:: If you wish to override a directory with another directory, you must make sure to attach the slash to the end of both the ``to_override`` specification and the ``override_with`` specification. If you fail to attach a slash to the end of a specification that points a directory, you will get unexpected results. You cannot override a directory specification with a file specification, and vice versa (a startup error will occur if you try). You cannot override a resource with itself (a startup error will occur if you try). Only individual *package* resources may be overridden. Overrides will not traverse through subpackages within an overridden package. This means that if you want to override resources for both ``some.package:templates``, and ``some.package.views:templates``, you will need to register two overrides. The package name in a specification may start with a dot, meaning that the package is relative to the package in which the ZCML file resides. For example:: Overrides for the same ``to_overrides`` specification can be named multiple times within ZCML. Each ``override_with`` path will be consulted in the order defined within ZCML, forming an override search path. Resource overrides can actually override resources other than templates. Any software which uses the ``pkg_resources`` ``get_resource_filename``, ``get_resource_stream`` or ``get_resource_string`` APIs will obtain an overridden file when an override is used. However, the only built-in facility which uses the ``pkg_resources`` API within BFG is the templating stuff, so we only call out template overrides here. - Use the ``pkg_resources`` API to locate template filenames instead of dead-reckoning using the ``os.path`` module. - The ``repoze.bfg.templating`` module now uses ``pkg_resources`` to locate and register template files instead of using an absolute path name. 1.0a4 (2009-06-25) ================== Features -------- - Cause ``:segment`` matches in route paths to put a Unicode-decoded and URL-dequoted value in the matchdict for the value matched. Previously a non-decoded non-URL-dequoted string was placed in the matchdict as the value. - Cause ``*remainder`` matches in route paths to put a *tuple* in the matchdict dictionary in order to be able to present Unicode-decoded and URL-dequoted values for the traversal path. Previously a non-decoded non-URL-dequoted string was placed in the matchdict as the value. - Add optional ``max_age`` keyword value to the ``remember`` method of ``repoze.bfg.authentication.AuthTktAuthenticationPolicy``; if this value is passed to ``remember``, the generated cookie will have a corresponding Max-Age value. Documentation ------------- - Add information to the URL Dispatch narrative documentation about path pattern matching syntax. Bug Fixes --------- - Make ``route_url`` URL-quote segment replacements during generation. Remainder segments are not quoted. 1.0a3 (2009-06-24) ================== Implementation Changes ---------------------- - ``repoze.bfg`` no longer relies on the Routes package to interpret URL paths. All known existing ``path`` patterns will continue to work with the reimplemented logic, which lives in ``repoze.bfg.urldispatch``. ```` ZCML directives which use certain attributes (uncommon ones) may not work (see "Backwards Incompatibilities" below). Bug Fixes --------- - ``model_url`` when passed a request that was generated as a result of a route match would fail in a call to ``route.generate``. - BFG-on-GAE didn't work due to a corner case bug in the fallback Python implementation of ``threading.local`` (symptom: "Initialization arguments are not supported"). Thanks to Michael Bernstein for the bug report. Documentation ------------- - Added a "corner case" explanation to the "Hybrid Apps" chapter explaining what to do when "the wrong" view is matched. - Use ``repoze.bfg.url.route_url`` API in tutorials rather than Routes ``url_for`` API. Features -------- - Added the ``repoze.bfg.url.route_url`` API. This API allows you to generate URLs based on ```` declarations. See the URL Dispatch narrative chapter and the "repoze.bfg.url" module API documentation for more information. Backwards Incompatibilities --------------------------- - As a result of disusing Routes, using the Routes ``url_for`` API inside a BFG application (as was suggested by previous iterations of tutorials) will no longer work. Use the ``repoze.bfg.url.route_url`` method instead. - The following attributes on the ```` ZCML directive no longer work: ``encoding``, ``static``, ``filter``, ``condition_method``, ``condition_subdomain``, ``condition_function``, ``explicit``, or ``subdomains``. These were all Routes features. - The ```` ZCML directive no longer supports the ```` subdirective. This was a Routes feature. 1.0a2 (2009-06-23) ================== Bug Fixes --------- - The ``bfg_routesalchemy`` paster template app tests failed due to a mismatch between test and view signatures. Features -------- - Add a ``view_for`` attribute to the ``route`` ZCML directive. This attribute should refer to an interface or a class (ala the ``for`` attribute of the ``view`` ZCML directive). Documentation ------------- - Conditional documentation in installation section ("how to install a Python interpreter"). Backwards Incompatibilities --------------------------- - The ``callback`` argument of the ``repoze.bfg.authentication`` authentication policies named ``RepozeWho1AuthenticationPolicy``, ``RemoteUserAuthenticationPolicy``, and ``AuthTktAuthenticationPolicy`` now must accept two positional arguments: the orginal argument accepted by each (userid or identity) plus a second argument, which will be the current request. Apologies, this is required to service finding groups when there is no "global" database connection. 1.0a1 (2009-06-22) ================== Features -------- - A new ZCML directive was added named ``notfound``. This ZCML directive can be used to name a view that should be invoked when the request can't otherwise be resolved to a view callable. For example:: - A new ZCML directive was added named ``forbidden``. This ZCML directive can be used to name a view that should be invoked when a view callable for a request is found, but cannot be invoked due to an authorization failure. For example:: - Allow views to be *optionally* defined as callables that accept only a request object, instead of both a context and a request (which still works, and always will). The following types work as views in this style: - functions that accept a single argument ``request``, e.g.:: def aview(request): pass - new and old-style classes that have an ``__init__`` method that accepts ``self, request``, e.g.:: def View(object): __init__(self, request): pass - Arbitrary callables that have a ``__call__`` method that accepts ``self, request``, e.g.:: def AView(object): def __call__(self, request): pass view = AView() This likely should have been the calling convention all along, as the request has ``context`` as an attribute already, and with views called as a result of URL dispatch, having the context in the arguments is not very useful. C'est la vie. - Cache the absolute path in the caller's package globals within ``repoze.bfg.path`` to get rid of repeated (expensive) calls to os.path.abspath. - Add ``reissue_time`` and ``timeout`` parameters to ``repoze.bfg.authentication.AuthTktAuthenticationPolicy`` constructor. If these are passed, cookies will be reset every so often (cadged from the same change to repoze.who lately). - The matchdict related to the matching of a Routes route is available on the request as the ``matchdict`` attribute: ``request.matchdict``. If no route matched, this attribute will be None. - Make 404 responses slightly cheaper by showing ``environ["PATH_INFO"]`` on the notfound result page rather than the fullly computed URL. - Move LRU cache implementation into a separate package (``repoze.lru``). - The concepts of traversal and URL dispatch have been unified. It is now possible to use the same sort of factory as both a traversal "root factory" and what used to be referred to as a urldispatch "context factory". - When the root factory argument (as a first argument) passed to ``repoze.bfg.router.make_app`` is ``None``, a *default* root factory is used. This is in support of using routes as "root finders"; it supplants the idea that there is a default ``IRoutesContextFactory``. - The `view`` ZCML statement and the ``repoze.bfg.view.bfg_view`` decorator now accept an extra argument: ``route_name``. If a ``route_name`` is specified, it must match the name of a previously defined ``route`` statement. When it is specified, the view will only be called when that route matches during a request. - It is now possible to perfom traversal *after* a route has matched. Use the pattern ``*traverse`` in a ```` ``path`` attribute within ZCML, and the path remainder which it matches will be used as a traversal path. - When any route defined matches, the WSGI environment will now contain a key ``bfg.routes.route`` (the Route object which matched), and a key ``bfg.routes.matchdict`` (the result of calling route.match). Deprecations ------------ - Utility registrations against ``repoze.bfg.interfaces.INotFoundView`` and ``repoze.bfg.interfaces.IForbiddenView`` are now deprecated. Use the ``notfound`` and ``forbidden`` ZCML directives instead (see the "Hooks" chapter for more information). Such registrations will continue to work, but the notfound and forbidden directives do "extra work" to ensure that the callable named by the directive can be called by the router even if it's a class or request-argument-only view. Removals -------- - The ``IRoutesContext``, ``IRoutesContextFactory``, and ``IContextNotFound`` interfaces were removed from ``repoze.bfg.interfaces``. These were never APIs. - The ``repoze.bfg.urldispatch.RoutesContextNotFound``, ``repoze.bfg.urldispatch.RoutesModelTraverser`` and ``repoze.bfg.urldispatch.RoutesContextURL`` classes were removed. These were also never APIs. Backwards Incompatibilities --------------------------- - Moved the ``repoze.bfg.push`` module, which implemented the ``pushpage`` decorator, into a separate distribution, ``repoze.bfg.pushpage``. Applications which used this decorator should continue to work after adding that distribution to their installation requirements. - Changing the default request factory via an IRequestFactory utility registration (as used to be documented in the "Hooks" chapter's "Changing the request factory" section) is no longer supported. The dance to manufacture a request is complicated as a result of unifying traversal and url dispatch, making it highly unlikely for anyone to be able to override it properly. For those who just want to decorate or modify a request, use a NewRequestEvent subscriber (see the Events chapter in the documentation). - The ``repoze.bfg.IRequestFactory`` interface was removed. See the bullet above for why. - Routes "context factories" (spelled as the factory argument to a route statement in ZCML) must now expect the WSGI environ as a single argument rather than a set of keyword arguments. They can obtain the match dictionary by asking for environ['bfg.routes.matchdict']. This is the same set of keywords that used to be passed to urldispatch "context factories" in BFG 0.9 and below. - Using the ``@zope.component.adapter`` decorator on a bfg view function no longer works. Use the ``@repoze.bfg.view.bfg_view`` decorator instead to mark a function (or a class) as a view. - The name under which the matching route object is found in the environ was changed from ``bfg.route`` to ``bfg.routes.route``. - Finding the root is now done *before* manufacturing a request object (and sending a new request event) within the router (it used to be performed afterwards). - Adding ``*path_info`` to a route no longer changes the PATH_INFO for a request that matches using URL dispatch. This feature was only there to service the ``repoze.bfg.wsgi.wsgiapp2`` decorator and it did it wrong; use ``*subpath`` instead now. - The values of ``subpath``, ``traversed``, and ``virtual_root_path`` attached to the request object are always now tuples instead of lists (performance). Bug Fixes --------- - The ``bfg_alchemy`` Paster template named "repoze.tm" in its pipeline rather than "repoze.tm2", causing the startup to fail. - Move BBB logic for registering an IAuthenticationPolicy/IForbiddenView/INotFoundView based on older concepts from the router module's ``make_app`` function into the ``repoze.bfg.zcml.zcml_configure`` callable, to service compatibility with scripts that use "zope.configuration.xmlconfig" (replace with ``repoze.bfg.zml.zcml_configure`` as necessary to get BBB logic) Documentation ------------- - Add interface docs related to how to create authentication policies and authorization policies to the "Security" narrative chapter. - Added a (fairly sad) "Combining Traversal and URL Dispatch" chapter to the narrative documentation. This explains the usage of ``*traverse`` and ``*subpath`` in routes URL patters. - A "router" chapter explaining the request/response lifecycle at a high level was added. - Replaced all mentions and explanations of a routes "context factory" with equivalent explanations of a "root factory" (context factories have been disused). - Updated Routes bfgwiki2 tutorial to reflect the fact that context factories are now no longer used. 0.9.1 (2009-06-02) ================== Features -------- - Add API named ``repoze.bfg.settings.get_settings`` which retrieves a derivation of values passed as the ``options`` value of ``repoze.bfg.router.make_app``. This API should be preferred instead of using getUtility(ISettings). I added a new ``repoze.bfg.settings`` API document as well. Bug Fixes --------- - Restored missing entry point declaration for bfg_alchemy paster template, which was accidentally removed in 0.9. Documentation ------------- - Fix a reference to ``wsgiapp`` in the ``wsgiapp2`` API documentation within the ``repoze.bfg.wsgi`` module. API Removals ------------ - The ``repoze.bfg.location.locate`` API was removed: it didn't do enough to be very helpful and had a misleading name. 0.9 (2009-06-01) ================ Bug Fixes --------- - It was not possible to register a custom ``IRoutesContextFactory`` for use as a default context factory as documented in the "Hooks" chapter. Features -------- - The ``request_type`` argument of ZCML ``view`` declarations and ``bfg_view`` decorators can now be one of the strings ``GET``, ``POST``, ``PUT``, ``DELETE``, or ``HEAD`` instead of a reference to the respective interface type imported from ``repoze.bfg.interfaces``. - The ``route`` ZCML directive now accepts ``request_type`` as an alias for its ``condition_method`` argument for symmetry with the ``view`` directive. - The ``bfg_routesalchemy`` paster template now provides a unit test and actually uses the database during a view rendering. Removals -------- - Remove ``repoze.bfg.threadlocal.setManager``. It was only used in unit tests. - Remove ``repoze.bfg.wsgi.HTTPException``, ``repoze.bfg.wsgi.NotFound``, and ``repoze.bfg.wsgi.Unauthorized``. These classes were disused with the introduction of the ``IUnauthorizedView`` and ``INotFoundView`` machinery. Documentation ------------- - Add description to narrative templating chapter about how to use Chameleon text templates. - Changed Views narrative chapter to use method strings rather than interface types, and moved advanced interface type usage to Events narrative chapter. - Added a Routes+SQLAlchemy wiki tutorial. 0.9a8 (2009-05-31) ================== Features -------- - It is now possible to register a custom ``repoze.bfg.interfaces.INotFoundView`` for a given application. This feature replaces the ``repoze.bfg.interfaces.INotFoundAppFactory`` feature previously described in the Hooks chapter. The INotFoundView will be called when the framework detects that a view lookup done as a result of a request fails; it should accept a context object and a request object; it should return an IResponse object (a webob response, basically). See the Hooks narrative chapter of the BFG docs for more info. - The error presented when a view invoked by the router returns a non-response object now includes the view's name for troubleshooting purposes. Bug Fixes --------- - A "new response" event is emitted for forbidden and notfound views. Deprecations ------------ - The ``repoze.bfg.interfaces.INotFoundAppFactory`` interface has been deprecated in favor of using the new ``repoze.bfg.interfaces.INotFoundView`` mechanism. Renames ------- - Renamed ``repoze.bfg.interfaces.IForbiddenResponseFactory`` to ``repoze.bfg.interfaces.IForbiddenView``. 0.9a7 (2009-05-30) ================== Features -------- - Remove "context" argument from ``effective_principals`` and ``authenticated_userid`` function APIs in ``repoze.bfg.security``, effectively a doing reversion to 0.8 and before behavior. Both functions now again accept only the ``request`` parameter. 0.9a6 (2009-05-29) ================== Documentation ------------- - Changed "BFG Wiki" tutorial to use AuthTktAuthenticationPolicy rather than repoze.who. Features -------- - Add an AuthTktAuthenticationPolicy. This policy retrieves credentials from an auth_tkt cookie managed by the application itself (instead of relying on an upstream data source for authentication data). See the Security API chapter of the documentation for more info. - Allow RemoteUserAuthenticationPolicy and RepozeWho1AuthenticationPolicy to accept various constructor arguments. See the Security API chapter of the documentation for more info. 0.9a5 (2009-05-28) ================== Features -------- - Add a ``get_app`` API functions to the ``paster`` module. This obtains a WSGI application from a config file given a config file name and a section name. See the ``repoze.bfg.paster`` API docs for more information. - Add a new module named ``scripting``. It contains a ``get_root`` API function, which, provided a Router instance, returns a traversal root object and a "closer". See the ``repoze.bfg.scripting`` API docs for more info. 0.9a4 (2009-05-27) ================== Bug Fixes --------- - Try checking for an "old style" security policy *after* we parse ZCML (thinko). 0.9a3 (2009-05-27) ================== Features -------- - Allow IAuthenticationPolicy and IAuthorizationPolicy to be overridden via ZCML registrations (do ZCML parsing after registering these in router.py). Documentation ------------- - Added "BFG Wiki" tutorial to documentation; it describes step-by-step how to create a traversal-based ZODB application with authentication. Deprecations ------------ - Added deprecations for imports of ``ACLSecurityPolicy``, ``InheritingACLSecurityPolicy``, ``RemoteUserACLSecurityPolicy``, ``RemoteUserInheritingACLSecurityPolicy``, ``WhoACLSecurityPolicy``, and ``WhoInheritingACLSecurityPolicy`` from the ``repoze.bfg.security`` module; for the meantime (for backwards compatibility purposes) these live in the ``repoze.bfg.secpols`` module. Note however, that the entire concept of a "security policy" is deprecated in BFG in favor of separate authentication and authorization policies, so any use of a security policy will generate additional deprecation warnings even if you do start using ``repoze.bfg.secpols``. ``repoze.bfg.secpols`` will disappear in a future release of ``repoze.bfg``. Deprecated Import Alias Removals -------------------------------- - Remove ``repoze.bfg.template`` module. All imports from this package have been deprecated since 0.3.8. Instead, import ``get_template``, ``render_template``, and ``render_template_to_response`` from the ``repoze.bfg.chameleon_zpt`` module. - Remove backwards compatibility import alias for ``repoze.bfg.traversal.split_path`` (deprecated since 0.6.5). This must now be imported as ``repoze.bfg.traversal.traversal_path``). - Remove backwards compatibility import alias for ``repoze.bfg.urldispatch.RoutesContext`` (deprecated since 0.6.5). This must now be imported as ``repoze.bfg.urldispatch.DefaultRoutesContext``. - Removed backwards compatibility import aliases for ``repoze.bfg.router.get_options`` and ``repoze.bfg.router.Settings`` (deprecated since 0.6.2). These both must now be imported from ``repoze.bfg.settings``. - Removed backwards compatibility import alias for ``repoze.bfg.interfaces.IRootPolicy`` (deprecated since 0.6.2). It must be imported as ``repoze.bfg.interfaces.IRootFactory`` now. - Removed backwards compatibility import alias for ``repoze.bfg.interfaces.ITemplate`` (deprecated since 0.4.4). It must be imported as ``repoze.bfg.interfaces.ITemplateRenderer`` now. - Removed backwards compatibility import alias for ``repoze.bfg.interfaces.ITemplateFactory`` (deprecated since 0.4.4). It must be imported as ``repoze.bfg.interfaces.ITemplateRendererFactory`` now. - Removed backwards compatibility import alias for ``repoze.bfg.chameleon_zpt.ZPTTemplateFactory`` (deprecated since 0.4.4). This must be imported as ``repoze.bfg.ZPTTemplateRenderer`` now. 0.9a2 (2009-05-27) ================== Features -------- - A paster command has been added named "bfgshell". This command can be used to get an interactive prompt with your BFG root object in the global namespace. E.g.:: bin/paster bfgshell /path/to/myapp.ini myapp See the ``Project`` chapter in the BFG documentation for more information. Deprecations ------------ - The name ``repoze.bfg.registry.registry_manager`` was never an API, but scripts in the wild were using it to set up an environment for use under a debug shell. A backwards compatibility shim has been added for this purpose, but the feature is deprecated. 0.9a1 (2009-5-27) ================= Features -------- - New API functions named ``forget`` and ``remember`` are available in the ``security`` module. The ``forget`` function returns headers which will cause the currently authenticated user to be logged out when set in a response. The ``remember`` function (when passed the proper arguments) will return headers which will cause a principal to be "logged in" when set in a response. See the Security API chapter of the docs for more info. - New keyword arguments to the ``repoze.bfg.router.make_app`` call have been added: ``authentication_policy`` and ``authorization_policy``. These should, respectively, be an implementation of an authentication policy (an object implementing the ``repoze.bfg.interfaces.IAuthenticationPolicy`` interface) and an implementation of an authorization policy (an object implementing ``repoze.bfg.interfaces.IAuthorizationPolicy)``. Concrete implementations of authentication policies exist in ``repoze.bfg.authentication``. Concrete implementations of authorization policies exist in ``repoze.bfg.authorization``. Both ``authentication_policy`` and ``authorization_policy`` default to ``None``. If ``authentication_policy`` is ``None``, but ``authorization_policy`` is *not* ``None``, then ``authorization_policy`` is ignored (the ability to do authorization depends on authentication). If the ``authentication_policy`` argument is *not* ``None``, and the ``authorization_policy`` argument *is* ``None``, the authorization policy defaults to an authorization implementation that uses ACLs (``repoze.bfg.authorization.ACLAuthorizationPolicy``). We no longer encourage configuration of "security policies" using ZCML, as previously we did for ``ISecurityPolicy``. This is because it's not uncommon to need to configure settings for concrete authorization or authentication policies using paste .ini parameters; the app entry point for your application is the natural place to do this. - Two new abstractions have been added in the way of adapters used by the system: an ``IAuthorizationPolicy`` and an ``IAuthenticationPolicy``. A combination of these (as registered by the ``securitypolicy`` ZCML directive) take the place of the ``ISecurityPolicy`` abstraction in previous releases of repoze.who. The API functions in ``repoze.who.security`` (such as ``authentication_userid``, ``effective_principals``, ``has_permission``, and so on) have been changed to try to make use of these new adapters. If you're using an older ``ISecurityPolicy`` adapter, the system will still work, but it will print deprecation warnings when such a policy is used. - The way the (internal) IViewPermission utilities registered via ZCML are invoked has changed. They are purely adapters now, returning a boolean result, rather than returning a callable. You shouldn't have been using these anyway. ;-) - New concrete implementations of IAuthenticationPolicy have been added to the ``repoze.bfg.authentication`` module: ``RepozeWho1AuthenticationPolicy`` which uses ``repoze.who`` identity to retrieve authentication data from and ``RemoteUserAuthenticationPolicy``, which uses the ``REMOTE_USER`` value in the WSGI environment to retrieve authentication data. - A new concrete implementation of IAuthorizationPolicy has been added to the ``repoze.bfg.authorization`` module: ``ACLAuthorizationPolicy`` which uses ACL inheritance to do authorization. - It is now possible to register a custom ``repoze.bfg.interfaces.IForbiddenResponseFactory`` for a given application. This feature replaces the ``repoze.bfg.interfaces.IUnauthorizedAppFactory`` feature previously described in the Hooks chapter. The IForbiddenResponseFactory will be called when the framework detects an authorization failure; it should accept a context object and a request object; it should return an IResponse object (a webob response, basically). Read the below point for more info and see the Hooks narrative chapter of the BFG docs for more info. Backwards Incompatibilities --------------------------- - Custom NotFound and Forbidden (nee' Unauthorized) WSGI applications (registered as a utility for INotFoundAppFactory and IUnauthorizedAppFactory) could rely on an environment key named ``message`` describing the circumstance of the response. This key has been renamed to ``repoze.bfg.message`` (as per the WSGI spec, which requires environment extensions to contain dots). Deprecations ------------ - The ``repoze.bfg.interfaces.IUnauthorizedAppFactory`` interface has been deprecated in favor of using the new ``repoze.bfg.interfaces.IForbiddenResponseFactory`` mechanism. - The ``view_execution_permitted`` API should now be imported from the ``repoze.bfg.security`` module instead of the ``repoze.bfg.view`` module. - The ``authenticated_userid`` and ``effective_principals`` APIs in ``repoze.bfg.security`` used to only take a single argument (request). They now accept two arguments (``context`` and ``request``). Calling them with a single argument is still supported but issues a deprecation warning. (NOTE: this change was reverted in 0.9a7; meaning the 0.9 versions of these functions again accept ``request`` only, just like 0.8 and before). - Use of "old-style" security policies (those base on ISecurityPolicy) is now deprecated. See the "Security" chapter of the docs for info about activating an authorization policy and an authentication poicy. 0.8.1 (2009-05-21) ================== Features -------- - Class objects may now be used as view callables (both via ZCML and via use of the ``bfg_view`` decorator in Python 2.6 as a class decorator). The calling semantics when using a class as a view callable is similar to that of using a class as a Zope "browser view": the class' ``__init__`` must accept two positional parameters (conventionally named ``context``, and ``request``). The resulting instance must be callable (it must have a ``__call__`` method). When called, the instance should return a response. For example:: from webob import Response class MyView(object): def __init__(self, context, request): self.context = context self.request = request def __call__(self): return Response('hello from %s!' % self.context) See the "Views" chapter in the documentation and the ``repoze.bfg.view`` API documentation for more information. - Removed the pickling of ZCML actions (the code that wrote ``configure.zcml.cache`` next to ``configure.zcml`` files in projects). The code which managed writing and reading of the cache file was a source of subtle bugs when users switched between imperative (e.g. ``@bfg_view``) registrations and declarative registrations (e.g. the ``view`` directive in ZCML) on the same project. On a moderately-sized project (535 ZCML actions and 15 ZCML files), executing actions read from the pickle was saving us only about 200ms (2.5 sec vs 2.7 sec average). On very small projects (1 ZCML file and 4 actions), startup time was comparable, and sometimes even slower when reading from the pickle, and both ways were so fast that it really just didn't matter anyway. 0.8 (2009-05-18) ================ Features -------- - Added a ``traverse`` function to the ``repoze.bfg.traversal`` module. This function may be used to retrieve certain values computed during path resolution. See the Traversal API chapter of the documentation for more information about this function. Deprecations ------------ - Internal: ``ITraverser`` callables should now return a dictionary rather than a tuple. Up until 0.7.0, all ITraversers were assumed to return a 3-tuple. In 0.7.1, ITraversers were assumed to return a 6-tuple. As (by evidence) it's likely we'll need to add further information to the return value of an ITraverser callable, 0.8 assumes that an ITraverser return a dictionary with certain elements in it. See the ``repoze.bfg.interfaces.ITraverser`` interface for the list of keys that should be present in the dictionary. ``ITraversers`` which return tuples will still work, although a deprecation warning will be issued. Backwards Incompatibilities --------------------------- - If your code used the ITraverser interface directly (not via an API function such as ``find_model``) via an adapter lookup, you'll need to change your code to expect a dictionary rather than a 3- or 6-tuple if your code ever gets return values from the default ModelGraphTraverser or RoutesModelTraverser adapters. 0.8a7 (2009-05-16) ================== Backwards Incompatibilities --------------------------- - The ``RoutesMapper`` class in ``repoze.bfg.urldispatch`` has been removed, as well as its documentation. It had been deprecated since 0.6.3. Code in ``repoze.bfg.urldispatch.RoutesModelTraverser`` which catered to it has also been removed. - The semantics of the ``route`` ZCML directive have been simplified. Previously, it was assumed that to use a route, you wanted to map a route to an externally registered view. The new ``route`` directive instead has a ``view`` attribute which is required, specifying the dotted path to a view callable. When a route directive is processed, a view is *registered* using the name attribute of the route directive as its name and the callable as its value. The ``view_name`` and ``provides`` attributes of the ``route`` directive are therefore no longer used. Effectively, if you were previously using the ``route`` directive, it means you must change a pair of ZCML directives that look like this:: To a ZCML directive that looks like this:: In other words, to make old code work, remove the ``view`` directives that were only there to serve the purpose of backing ``route`` directives, and move their ``view=`` attribute into the ``route`` directive itself. This change also necessitated that the ``name`` attribute of the ``route`` directive is now required. If you were previously using ``route`` directives without a ``name`` attribute, you'll need to add one (the name is arbitrary, but must be unique among all ``route`` and ``view`` statements). The ``provides`` attribute of the ``route`` directive has also been removed. This directive specified a sequence of interface types that the generated context would be decorated with. Since route views are always generated now for a single interface (``repoze.bfg.IRoutesContext``) as opposed to being looked up arbitrarily, there is no need to decorate any context to ensure a view is found. Documentation ------------- - Added API docs for the ``repoze.bfg.testing`` methods ``registerAdapter``, ``registerUtiity``, ``registerSubscriber``, and ``cleanUp``. - Added glossary entry for "root factory". - Noted existence of ``repoze.bfg.pagetemplate`` template bindings in "Available Add On Template System Bindings" in Templates chapter in narrative docs. - Update "Templates" narrative chapter in docs (expand to show a sample template and correct macro example). Features -------- - Courtesty Carlos de la Guardia, added an ``alchemy`` Paster template. This paster template sets up a BFG project that uses SQAlchemy (with SQLite) and uses traversal to resolve URLs. (no Routes areused). This template can be used via ``paster create -t bfg_alchemy``. - The Routes ``Route`` object used to resolve the match is now put into the environment as ``bfg.route`` when URL dispatch is used. - You can now change the default Routes "context factory" globally. See the "ZCML Hooks" chapter of the documentation (in the "Changing the Default Routes Context Factory" section). 0.8a6 (2009-05-11) ================== Features -------- - Added a ``routesalchemy`` Paster template. This paster template sets up a BFG project that uses SQAlchemy (with SQLite) and uses Routes exclusively to resolve URLs (no traversal root factory is used). This template can be used via ``paster create -t bfg_routesalchemy``. Documentation ------------- - Added documentation to the URL Dispatch chapter about how to catch the root URL using a ZCML ``route`` directive. - Added documentation to the URL Dispatch chapter about how to perform a cleanup function at the end of a request (e.g. close the SQL connection). Bug Fixes --------- - In version 0.6.3, passing a ``get_root`` callback (a "root factory") to ``repoze.bfg.router.make_app`` became optional if any ``route`` declaration was made in ZCML. The intent was to make it possible to disuse traversal entirely, instead relying entirely on URL dispatch (Routes) to resolve all contexts. However a compound set of bugs prevented usage of a Routes-based root view (a view which responds to "/"). One bug existed in `repoze.bfg.urldispatch``, another existed in Routes itself. To resolve this issue, the urldispatch module was fixed, and a fork of the Routes trunk was put into the "dev" index named ``Routes-1.11dev-chrism-home``. The source for the fork exists at `http://bitbucket.org/chrism/routes-home/ `_; its contents have been merged into the Routes trunk (what will be Routes 1.11). 0.8a5 (2009-05-08) ================== Features -------- - Two new security policies were added: RemoteUserInheritingACLSecurityPolicy and WhoInheritingACLSecurityPolicy. These are security policies which take into account *all* ACLs defined in the lineage of a context rather than stopping at the first ACL found in a lineage. See the "Security" chapter of the API documentation for more information. - The API and narrative documentation dealing with security was changed to introduce the new "inheriting" security policy variants. - Added glossary entry for "lineage". Deprecations ------------ - The security policy previously named ``RepozeWhoIdentityACLSecurityPolicy`` now has the slightly saner name of ``WhoACLSecurityPolicy``. A deprecation warning is emitted when this policy is imported under the "old" name; usually this is due to its use in ZCML within your application. If you're getting this deprecation warning, change your ZCML to use the new name, e.g. change:: To:: 0.8a4 (2009-05-04) ================== Features -------- - ``zope.testing`` is no longer a direct dependency, although our dependencies (such as ``zope.interface``, ``repoze.zcml``, etc) still depend on it. - Tested on Google App Engine. Added a tutorial to the documentation explaining how to deploy a BFG app to GAE. Backwards Incompatibilities --------------------------- - Applications which rely on ``zope.testing.cleanup.cleanUp`` in unit tests can still use that function indefinitely. However, for maximum forward compatibility, they should import ``cleanUp`` from ``repoze.bfg.testing`` instead of from ``zope.testing.cleanup``. The BFG paster templates and docs have been changed to use this function instead of the ``zope.testing.cleanup`` version. 0.8a3 (2009-05-03) =================== Features -------- - Don't require a successful import of ``zope.testing`` at BFG application runtime. This allows us to get rid of ``zope.testing`` on platforms like GAE which have file limits. 0.8a2 (2009-05-02) ================== Features -------- - We no longer include the ``configure.zcml`` of the ``chameleon.zpt`` package within the ``configure.zcml`` of the "repoze.bfg.includes" package. This has been a no-op for some time now. - The ``repoze.bfg.chameleon_zpt`` package no longer imports from ``chameleon.zpt`` at module scope, deferring the import until later within a method call. The ``chameleon.zpt`` package can't be imported on platforms like GAE. 0.8a1 (2009-05-02) ================== Deprecation Warning and Import Alias Removals --------------------------------------------- - Since version 0.6.1, a deprecation warning has been emitted when the name ``model_url`` is imported from the ``repoze.bfg.traversal`` module. This import alias (and the deprecation warning) has been removed. Any import of the ``model_url`` function will now need to be done from ``repoze.bfg.url``; any import of the name ``model_url`` from ``repoze.bfg.traversal`` will now fail. This was done to remove a dependency on zope.deferredimport. - Since version 0.6.5, a deprecation warning has been emitted when the name ``RoutesModelTraverser`` is imported from the ``repoze.bfg.traversal`` module. This import alias (and the deprecation warning) has been removed. Any import of the ``RoutesModelTraverser`` class will now need to be done from ``repoze.bfg.urldispatch``; any import of the name ``RoutesModelTraverser`` from ``repoze.bfg.traversal`` will now fail. This was done to remove a dependency on zope.deferredimport. Features -------- - This release of ``repoze.bfg`` is "C-free". This means it has no hard dependencies on any software that must be compiled from C source at installation time. In particular, ``repoze.bfg`` no longer depends on the ``lxml`` package. This change has introduced some backwards incompatibilities, described in the "Backwards Incompatibilities" section below. - This release was tested on Windows XP. It appears to work fine and all the tests pass. Backwards Incompatibilities --------------------------- Incompatibilities related to making ``repoze.bfg`` "C-free": - Removed the ``repoze.bfg.chameleon_genshi`` module, and thus support for Genshi-style chameleon templates. Genshi-style Chameleon templates depend upon ``lxml``, which is implemented in C (as opposed to pure Python) and the ``repoze.bfg`` core is "C-free" as of this release. You may get Genshi-style Chameleon support back by installing the ``repoze.bfg.chameleon_genshi`` package availalable from http://svn.repoze.org/repoze.bfg.chameleon_genshi (also available in the index at http://dist.repoze.org/bfg/0.8/simple). All existing code that depended on the ``chameleon_genshi`` module prior to this release of ``repoze.bfg`` should work without change after this addon is installed. - Removed the ``repoze.bfg.xslt`` module and thus support for XSL templates. The ``repoze.bfg.xslt`` module depended upon ``lxml``, which is implemented in C, and the ``repoze.bfg`` core is "C-free" as of this release. You bay get XSL templating back by installing the ``repoze.bfg.xslt`` package available from http://svn.repoze.org/repoze.bfg.xslt/ (also available in the index at http://dist.repoze.org/bfg/0.8/simple). All existing code that depended upon the ``xslt`` module prior to this release of ``repoze.bfg`` should work without modification after this addon is installed. - Removed the ``repoze.bfg.interfaces.INodeTemplateRenderer`` interface and the an old b/w compat aliases from that interface to ``repoze.bfg.interfaces.INodeTemplate``. This interface must now be imported from the ``repoze.bfg.xslt.interfaces`` package after installation of the ``repoze.bfg.xslt`` addon package described above as ``repoze.bfg.interfaces.INodeTemplateRenderer``. This interface was never part of any public API. Other backwards incompatibilities: - The ``render_template`` function in ``repoze.bfg.chameleon_zpt`` returns Unicode instead of a string. Likewise, the individual values returned by the iterable created by the ``render_template_to_iterable`` function are also each Unicode. This is actually a backwards incompatibility inherited from our new use of the combination of ``chameleon.core`` 1.0b32 (the non-lxml-depending version) and ``chameleon.zpt`` 1.0b16+ ; the ``chameleon.zpt`` PageTemplateFile implementation used to return a string, but now returns Unicode. 0.7.1 (2009-05-01) ================== Index-Related ------------- - The canonical package index location for ``repoze.bfg`` has changed. The "old" index (http://dist.repoze.org/lemonade/dev/simple) has been superseded by a new index location (`http://dist.repoze.org/bfg/current/simple `_). The installation documentation has been updated as well as the ``setup.cfg`` file in this package. The "lemonade" index still exists, but it is not guaranteed to have the latest BFG software in it, nor will it be maintained in the future. Features -------- - The "paster create" templates have been modified to use links to the new "bfg.repoze.org" and "docs.repoze.org" websites. - Added better documentation for virtual hosting at a URL prefix within the virtual hosting docs chapter. - The interface for ``repoze.bfg.interfaces.ITraverser`` and the built-in implementations that implement the interface (``repoze.bfg.traversal.ModelGraphTraverser``, and ``repoze.bfg.urldispatch.RoutesModelTraverser``) now expect the ``__call__`` method of an ITraverser to return 3 additional arguments: ``traversed``, ``virtual_root``, and ``virtual_root_path`` (the old contract was that the ``__call__`` method of an ITraverser returned; three arguments, the contract new is that it returns six). ``traversed`` will be a sequence of Unicode names that were traversed (including the virtual root path, if any) or ``None`` if no traversal was performed, ``virtual_root`` will be a model object representing the virtual root (or the physical root if traversal was not performed), and ``virtual_root_path`` will be a sequence representing the virtual root path (a sequence of Unicode names) or ``None`` if traversal was not performed. Six arguments are now returned from BFG ITraversers. They are returned in this order: ``context``, ``view_name``, ``subpath``, ``traversed``, ``virtual_root``, and ``virtual_root_path``. Places in the BFG code which called an ITraverser continue to accept a 3-argument return value, although BFG will generate and log a warning when one is encountered. - The request object now has the following attributes: ``traversed`` (the sequence of names traversed or ``None`` if traversal was not performed), ``virtual_root`` (the model object representing the virtual root, including the virtual root path if any), and ``virtual_root_path`` (the seuquence of names representing the virtual root path or ``None`` if traversal was not performed). - A new decorator named ``wsgiapp2`` was added to the ``repoze.bfg.wsgi`` module. This decorator performs the same function as ``repoze.bfg.wsgi.wsgiapp`` except it fixes up the ``SCRIPT_NAME``, and ``PATH_INFO`` environment values before invoking the WSGI subapplication. - The ``repoze.bfg.testing.DummyRequest`` object now has default attributes for ``traversed``, ``virtual_root``, and ``virtual_root_path``. - The RoutesModelTraverser now behaves more like the Routes "RoutesMiddleware" object when an element in the match dict is named ``path_info`` (usually when there's a pattern like ``http://foo/*path_info``). When this is the case, the ``PATH_INFO`` environment variable is set to the value in the match dict, and the ``SCRIPT_NAME`` is appended to with the prefix of the original ``PATH_INFO`` not including the value of the new variable. - The notfound debug now shows the traversed path, the virtual root, and the virtual root path too. - Speed up / clarify 'traversal' module's 'model_path', 'model_path_tuple', and '_model_path_list' functions. Backwards Incompatibilities --------------------------- - In previous releases, the ``repoze.bfg.url.model_url``, ``repoze.bfg.traversal.model_path`` and ``repoze.bfg.traversal.model_path_tuple`` functions always ignored the ``__name__`` argument of the root object in a model graph ( effectively replacing it with a leading ``/`` in the returned value) when a path or URL was generated. The code required to perform this operation was not efficient. As of this release, the root object in a model graph *must* have a ``__name__`` attribute that is either ``None`` or the empty string (``''``) for URLs and paths to be generated properly from these APIs. If your root model object has a ``__name__`` argument that is not one of these values, you will need to change your code for URLs and paths to be generated properly. If your model graph has a root node with a string ``__name__`` that is not null, the value of ``__name__`` will be prepended to every path and URL generated. - The ``repoze.bfg.location.LocationProxy`` class and the ``repoze.bfg.location.ClassAndInstanceDescr`` class have both been removed in order to be able to eventually shed a dependency on ``zope.proxy``. Neither of these classes was ever an API. - In all previous releases, the ``repoze.bfg.location.locate`` function worked like so: if a model did not explicitly provide the ``repoze.bfg.interfaces.ILocation`` interface, ``locate`` returned a ``LocationProxy`` object representing ``model`` with its ``__parent__`` attribute assigned to ``parent`` and a ``__name__`` attribute assigned to ``__name__``. In this release, the ``repoze.bfg.location.locate`` function simply jams the ``__name__`` and ``__parent__`` attributes on to the supplied model unconditionally, no matter if the object implements ILocation or not, and it never returns a proxy. This was done because the LocationProxy behavior has now moved into an add-on package (``repoze.bfg.traversalwrapper``), in order to eventually be able to shed a dependency on ``zope.proxy``. - In all previous releases, by default, if traversal was used (as opposed to URL-dispatch), and the root object supplied the``repoze.bfg.interfaces.ILocation`` interface, but the children returned via its ``__getitem__`` returned an object that did not implement the same interface, ``repoze.bfg`` provided some implicit help during traversal. This traversal feature wrapped subobjects from the root (and thereafter) that did not implement ``ILocation`` in proxies which automatically provided them with a ``__name__`` and ``__parent__`` attribute based on the name being traversed and the previous object traversed. This feature has now been removed from the base ``repoze.bfg`` package for purposes of eventually shedding a dependency on ``zope.proxy``. In order to re-enable the wrapper behavior for older applications which cannot be changed, register the "traversalwrapper" ``ModelGraphTraverser`` as the traversal policy, rather than the default ``ModelGraphTraverser``. To use this feature, you will need to install the ``repoze.bfg.traversalwrapper`` package (an add-on package, available at http://svn.repoze.org/repoze.bfg.traversalwrapper) Then change your application's ``configure.zcml`` to include the following stanza: When this ITraverserFactory is used instead of the default, no object in the graph (even the root object) must supply a ``__name__`` or ``__parent__`` attribute. Even if subobjects returned from the root *do* implement the ILocation interface, these will still be wrapped in proxies that override the object's "real" ``__parent__`` and ``__name__`` attributes. See also changes to the "Models" chapter of the documentation (in the "Location-Aware Model Instances") section. 0.7.0 (2009-04-11) ================== Bug Fixes --------- - Fix a bug in ``repoze.bfg.wsgi.HTTPException``: the content length was returned as an int rather than as a string. - Add explicit dependencies on ``zope.deferredimport``, ``zope.deprecation``, and ``zope.proxy`` for forward compatibility reasons (``zope.component`` will stop relying on ``zope.deferredimport`` soon and although we use it directly, it's only a transitive dependency, and ''zope.deprecation`` and ``zope.proxy`` are used directly even though they're only transitive dependencies as well). - Using ``model_url`` or ``model_path`` against a broken model graph (one with models that had a non-root model with a ``__name__`` of ``None``) caused an inscrutable error to be thrown: ( if not ``_must_quote[cachekey].search(s): TypeError: expected string or buffer``). Now URLs and paths generated against graphs that have None names in intermediate nodes will replace the None with the empty string, and, as a result, the error won't be raised. Of course the URL or path will still be bogus. Features -------- - Make it possible to have ``testing.DummyTemplateRenderer`` return some nondefault string representation. - Added a new ``anchor`` keyword argument to ``model_url``. If ``anchor`` is present, its string representation will be used as a named anchor in the generated URL (e.g. if ``anchor`` is passed as ``foo`` and the model URL is ``http://example.com/model/url``, the generated URL will be ``http://example.com/model/url#foo``). Backwards Incompatibilities --------------------------- - The default request charset encoding is now ``utf-8``. As a result, the request machinery will attempt to decode values from the utf-8 encoding to Unicode automatically when they are obtained via ``request.params``, ``request.GET``, and ``request.POST``. The previous behavior of BFG was to return a bytestring when a value was accessed in this manner. This change will break form handling code in apps that rely on values from those APIs being considered bytestrings. If you are manually decoding values from form submissions in your application, you'll either need to change the code that does that to expect Unicode values from ``request.params``, ``request.GET`` and ``request.POST``, or you'll need to explicitly reenable the previous behavior. To reenable the previous behavior, add the following to your application's ``configure.zcml``:: See also the documentation in the "Views" chapter of the BFG docs entitled "Using Views to Handle Form Submissions (Unicode and Character Set Issues)". Documentation ------------- - Add a section to the narrative Views chapter entitled "Using Views to Handle Form Submissions (Unicode and Character Set Issues)" explaining implicit decoding of form data values. 0.6.9 (2009-02-16) ================== Bug Fixes --------- - lru cache was unstable under concurrency (big surprise!) when it tried to redelete a key in the cache that had already been deleted. Symptom: line 64 in put:del data[oldkey]:KeyError: '/some/path'. Now we just ignore the key error if we can't delete the key (it has already been deleted). - Empty location names in model paths when generating a URL using ``repoze.bfg.model_url`` based on a model obtained via traversal are no longer ignored in the generated URL. This means that if a non-root model object has a ``__name__`` of ``''``, the URL will reflect it (e.g. ``model_url`` will generate ``http://foo/bar//baz`` if an object with the ``__name__`` of ``''`` is a child of bar and the parent of baz). URLs generated with empty path segments are, however, still irresolveable by the model graph traverser on request ingress (the traverser strips empty path segment names). Features -------- - Microspeedups of ``repoze.bfg.traversal.model_path``, ``repoze.bfg.traversal.model_path_tuple``, ``repoze.bfg.traversal.quote_path_segment``, and ``repoze.bfg.url.urlencode``. - add zip_safe = false to setup.cfg. Documentation ------------- - Add a note to the ``repoze.bfg.traversal.quote_path_segment`` API docs about caching of computed values. Implementation Changes ---------------------- - Simplification of ``repoze.bfg.traversal.TraversalContextURL.__call__`` (it now uses ``repoze.bfg.traversal.model_path`` instead of rolling its own path-generation). 0.6.8 (2009-02-05) ================== Backwards Incompatibilities --------------------------- - The ``repoze.bfg.traversal.model_path`` API now returns a *quoted* string rather than a string represented by series of unquoted elements joined via ``/`` characters. Previously it returned a string or unicode object representing the model path, with each segment name in the path joined together via ``/`` characters, e.g. ``/foo /bar``. Now it returns a string, where each segment is a UTF-8 encoded and URL-quoted element e.g. ``/foo%20/bar``. This change was (as discussed briefly on the repoze-dev maillist) necessary to accomodate model objects which themselves have ``__name__`` attributes that contain the ``/`` character. For people that have no models that have high-order Unicode ``__name__`` attributes or ``__name__`` attributes with values that require URL-quoting with in their model graphs, this won't cause any issue. However, if you have code that currently expects ``model_path`` to return an unquoted string, or you have an existing application with data generated via the old method, and you're too lazy to change anything, you may wish replace the BFG-imported ``model_path`` in your code with this function (this is the code of the "old" ``model_path`` implementation):: from repoze.bfg.location import lineage def i_am_too_lazy_to_move_to_the_new_model_path(model, *elements): rpath = [] for location in lineage(model): if location.__name__: rpath.append(location.__name__) path = '/' + '/'.join(reversed(rpath)) if elements: suffix = '/'.join(elements) path = '/'.join([path, suffix]) return path - The ``repoze.bfg.traversal.find_model`` API no longer implicitly converts unicode representations of a full path passed to it as a Unicode object into a UTF-8 string. Callers should either use prequoted path strings returned by ``repoze.bfg.traversal.model_path``, or tuple values returned by the result of ``repoze.bfg.traversal.model_path_tuple`` or they should use the guidelines about passing a string ``path`` argument described in the ``find_model`` API documentation. Bugfixes -------- - Each argument contained in ``elements`` passed to ``repoze.bfg.traversal.model_path`` will now have any ``/`` characters contained within quoted to ``%2F`` in the returned string. Previously, ``/`` characters in elements were left unquoted (a bug). Features -------- - A ``repoze.bfg.traversal.model_path_tuple`` API was added. This API is an alternative to ``model_path`` (which returns a string); ``model_path_tuple`` returns a model path as a tuple (much like Zope's ``getPhysicalPath``). - A ``repoze.bfg.traversal.quote_path_segment`` API was added. This API will quote an individual path segment (string or unicode object). See the ``repoze.bfg.traversal`` API documentation for more information. - The ``repoze.bfg.traversal.find_model`` API now accepts "path tuples" (see the above note regarding ``model_path_tuple``) as well as string path representations (from ``repoze.bfg.traversal.model_path``) as a ``path`` argument. - Add ` `renderer`` argument (defaulting to None) to ``repoze.bfg.testing.registerDummyRenderer``. This makes it possible, for instance, to register a custom renderer that raises an exception in a unit test. Implementation Changes ---------------------- - Moved _url_quote function back to ``repoze.bfg.traversal`` from ``repoze.bfg.url``. This is not an API. 0.6.7 (2009-01-27) ================== Features -------- - The ``repoze.bfg.url.model_url`` API now works against contexts derived from Routes URL dispatch (``Routes.util.url_for`` is called under the hood). - "Virtual root" support for traversal-based applications has been added. Virtual root support is useful when you'd like to host some model in a ``repoze.bfg`` model graph as an application under a URL pathname that does not include the model path itself. For more information, see the (new) "Virtual Hosting" chapter in the documentation. - A ``repoze.bfg.traversal.virtual_root`` API has been added. When called, it returns the virtual root object (or the physical root object if no virtual root has been specified). Implementation Changes ---------------------- - ``repoze.bfg.traversal.RoutesModelTraverser`` has been moved to ``repoze.bfg.urldispatch``. - ``model_url`` URL generation is now performed via an adapter lookup based on the context and the request. - ZCML which registers two adapters for the ``IContextURL`` interface has been added to the configure.zcml in ``repoze.bfg.includes``. 0.6.6 (2009-01-26) ================== Implementation Changes ---------------------- - There is an indirection in ``repoze.bfg.url.model_url`` now that consults a utility to generate the base model url (without extra elements or a query string). Eventually this will service virtual hosting; for now it's undocumented and should not be hooked. 0.6.5 (2009-01-26) ================== Features -------- - You can now override the NotFound and Unauthorized responses that ``repoze.bfg`` generates when a view cannot be found or cannot be invoked due to lack of permission. See the "ZCML Hooks" chapter in the docs for more information. - Added Routes ZCML directive attribute explanations in documentation. - Added a ``traversal_path`` API to the traversal module; see the "traversal" API chapter in the docs. This was a function previously known as ``split_path`` that was not an API but people were using it anyway. Unlike ``split_path``, it now returns a tuple instead of a list (as its values are cached). Behavior Changes ---------------- - The ``repoze.bfg.view.render_view_to_response`` API will no longer raise a ValueError if an object returned by a view function it calls does not possess certain attributes (``headerlist``, ``app_iter``, ``status``). This API used to attempt to perform a check using the ``is_response`` function in ``repoze.bfg.view``, and raised a ``ValueError`` if the ``is_response`` check failed. The responsibility is now the caller's to ensure that the return value from a view function is a "real" response. - WSGI environ dicts passed to ``repoze.bfg`` 's Router must now contain a REQUEST_METHOD key/value; if they do not, a KeyError will be raised (speed). - It is no longer permissible to pass a "nested" list of principals to ``repoze.bfg.ACLAuthorizer.permits`` (e.g. ``['fred', ['larry', 'bob']]``). The principals list must be fully expanded. This feature was never documented, and was never an API, so it's not a backwards incompatibility. - It is no longer permissible for a security ACE to contain a "nested" list of permissions (e.g. ``(Allow, Everyone, ['read', ['view', ['write', 'manage']]])`)`. The list must instead be fully expanded (e.g. ``(Allow, Everyone, ['read', 'view', 'write', 'manage])``). This feature was never documented, and was never an API, so it's not a backwards incompatibility. - The ``repoze.bfg.urldispatch.RoutesRootFactory`` now injects the ``wsgiorg.routing_args`` environment variable into the environ when a route matches. This is a tuple of ((), routing_args) where routing_args is the value that comes back from the routes mapper match (the "match dict"). - The ``repoze.bfg.traversal.RoutesModelTraverser`` class now wants to obtain the ``view_name`` and ``subpath`` from the ``wsgiorgs.routing_args`` environment variable. It falls back to obtaining these from the context for backwards compatibility. Implementation Changes ---------------------- - Get rid of ``repoze.bfg.security.ACLAuthorizer``: the ``ACLSecurityPolicy`` now does what it did inline. - Get rid of ``repoze.bfg.interfaces.NoAuthorizationInformation`` exception: it was used only by ``ACLAuthorizer``. - Use a homegrown NotFound error instead of ``webob.exc.HTTPNotFound`` (the latter is slow). - Use a homegrown Unauthorized error instead of ``webob.exc.Unauthorized`` (the latter is slow). - the ``repoze.bfg.lru.lru_cached`` decorator now uses functools.wraps in order to make documentation of LRU-cached functions possible. - Various speed micro-tweaks. Bug Fixes --------- - ``repoze.bfg.testing.DummyModel`` did not have a ``get`` method; it now does. 0.6.4 (2009-01-23) ================== Backwards Incompatibilities --------------------------- - The ``unicode_path_segments`` configuration variable and the ``BFG_UNICODE_PATH_SEGMENTS`` configuration variable have been removed. Path segments are now always passed to model ``__getitem__`` methods as unicode. "True" has been the default for this setting since 0.5.4, but changing this configuration setting to false allowed you to go back to passing raw path element strings to model ``__getitem__`` methods. Removal of this knob services a speed goal (we get about +80 req/s by removing the check), and it's clearer just to always expect unicode path segments in model ``__getitem__`` methods. Implementation Changes ---------------------- - ``repoze.bfg.traversal.split_path`` now also handles decoding path segments to unicode (for speed, because its results are cached). - ``repoze.bfg.traversal.step`` was made a method of the ModelGraphTraverser. - Use "precooked" Request subclasses (e.g. ``repoze.bfg.request.GETRequest``) that correspond to HTTP request methods within ``router.py`` when constructing a request object rather than using ``alsoProvides`` to attach the proper interface to an unsubclassed ``webob.Request``. This pattern is purely an optimization (e.g. preventing calls to ``alsoProvides`` means the difference between 590 r/s and 690 r/s on a MacBook 2GHz). - Tease out an extra 4% performance boost by changing the Router; instead of using imported ZCA APIs, use the same APIs directly against the registry that is an attribute of the Router. - The registry used by BFG is now a subclass of ``zope.component.registry.Components`` (defined as ``repoze.bfg.registry.Registry``); it has a ``notify`` method, a ``registerSubscriptionAdapter`` and a ``registerHandler`` method. If no subscribers are registered via ``registerHandler`` or ``registerSubscriptionAdapter``, ``notify`` is a noop for speed. - The Allowed and Denied classes in ``repoze.bfg.security`` now are lazier about constructing the representation of a reason message for speed; ``repoze.bfg.view_execution_permitted`` takes advantage of this. - The ``is_response`` check was sped up by about half at the expense of making its code slightly uglier. New Modules ----------- - ``repoze.bfg.lru`` implements an LRU cache class and a decorator for internal use. 0.6.3 (2009-01-19) ================== Bug Fixes --------- - Readd ``root_policy`` attribute on Router object (as a property which returns the IRootFactory utility). It was inadvertently removed in 0.6.2. Code in the wild depended upon its presence (esp. scripts and "debug" helpers). Features -------- - URL-dispatch has been overhauled: it is no longer necessary to manually create a RoutesMapper in your application's entry point callable in order to use URL-dispatch (aka `Routes `_). A new ``route`` directive has been added to the available list of ZCML directives. Each ``route`` directive inserted into your application's ``configure.zcml`` establishes a Routes mapper connection. If any ``route`` declarations are made via ZCML within a particular application, the ``get_root`` callable passed in to ``repoze.bfg.router.make_app`` will automatically be wrapped in the equivalent of a RoutesMapper. Additionally, the new ``route`` directive allows the specification of a ``context_interfaces`` attribute for a route, this will be used to tag the manufactured routes context with specific interfaces when a route specifying a ``context_interfaces`` attribute is matched. - A new interface ``repoze.bfg.interfaces.IContextNotFound`` was added. This interface is attached to a "dummy" context generated when Routes cannot find a match and there is no "fallback" get_root callable that uses traversal. - The ``bfg_starter`` and ``bfg_zodb`` "paster create" templates now contain images and CSS which are displayed when the default page is displayed after initial project generation. - Allow the ``repoze.bfg.view.static`` helper to be passed a relative ``root_path`` name; it will be considered relative to the file in which it was called. - The functionality of ``repoze.bfg.convention`` has been merged into the core. Applications which make use of ``repoze.bfg.convention`` will continue to work indefinitely, but it is recommended that apps stop depending upon it. To do so, substitute imports of ``repoze.bfg.convention.bfg_view`` with imports of ``repoze.bfg.view.bfg_view``, and change the stanza in ZCML from ```` to ````. As a result of the merge, bfg has grown a new dependency: ``martian``. - View functions which use the pushpage decorator are now pickleable (meaning their use won't prevent a ``configure.zcml.cache`` file from being written to disk). - Instead of invariably using ``webob.Request`` as the "request factory" (e.g. in the ``Router`` class) and ``webob.Response`` and the "response factory" (e.g. in ``render_template_to_response``), allow both to be overridden via a ZCML utility hook. See the "Using ZCML Hooks" chapter of the documentation for more information. Deprecations ------------ - The class ``repoze.bfg.urldispatch.RoutesContext`` has been renamed to ``repoze.bfg.urldispatch.DefaultRoutesContext``. The class should be imported by the new name as necessary (although in reality it probably shouldn't be imported from anywhere except internally within BFG, as it's not part of the API). Implementation Changes ---------------------- - The ``repoze.bfg.wsgi.wsgiapp`` decorator now uses ``webob.Request.get_response`` to do its work rather than relying on homegrown WSGI code. - The ``repoze.bfg.view.static`` helper now uses ``webob.Request.get_response`` to do its work rather than relying on homegrown WSGI code. - The ``repoze.bfg.urldispatch.RoutesModelTraverser`` class has been moved to ``repoze.bfg.traversal.RoutesModelTraverser``. - The ``repoze.bfg.registry.makeRegistry`` function was renamed to ``repoze.bfg.registry.populateRegistry`` and now accepts a ``registry`` argument (which should be an instance of ``zope.component.registry.Components``). Documentation Additions ----------------------- - Updated narrative urldispatch chapter with changes required by ```` ZCML directive. - Add a section on "Using BFG Security With URL Dispatch" into the urldispatch chapter of the documentation. - Better documentation of security policy implementations that ship with repoze.bfg. - Added a "Using ZPT Macros in repoze.bfg" section to the narrative templating chapter. 0.6.2 (2009-01-13) ================== Features -------- - Tests can be run with coverage output if you've got ``nose`` installed in the interpreter which you use to run tests. Using an interpreter with ``nose`` installed, do ``python setup.py nosetests`` within a checkout of the ``repoze.bfg`` package to see test coverage output. - Added a ``post`` argument to the ``repoze.bfg.testing:DummyRequest`` constructor. - Added ``__len__`` and ``__nonzero__`` to ``repoze.bfg.testing:DummyModel``. - The ``repoze.bfg.registry.get_options`` callable (now renamed to ``repoze.bfg.setings.get_options``) used to return only framework-specific keys and values in the dictionary it returned. It now returns all the keys and values in the dictionary it is passed *plus* any framework-specific settings culled from the environment. As a side effect, all PasteDeploy application-specific config file settings are made available as attributes of the ``ISettings`` utility from within BFG. - Renamed the existing BFG paster template to ``bfg_starter``. Added another template (``bfg_zodb``) showing default ZODB setup using ``repoze.zodbconn``. - Add a method named ``assert_`` to the DummyTemplateRenderer. This method accepts keyword arguments. Each key/value pair in the keyword arguments causes an assertion to be made that the renderer received this key with a value equal to the asserted value. - Projects generated by the paster templates now use the ``DummyTemplateRenderer.assert_`` method in their view tests. - Make the (internal) thread local registry manager maintain a stack of registries in order to make it possible to call one BFG application from inside another. - An interface specific to the HTTP verb (GET/PUT/POST/DELETE/HEAD) is attached to each request object on ingress. The HTTP-verb-related interfaces are defined in ``repoze.bfg.interfaces`` and are ``IGETRequest``, ``IPOSTRequest``, ``IPUTRequest``, ``IDELETERequest`` and ``IHEADRequest``. These interfaces can be specified as the ``request_type`` attribute of a bfg view declaration. A view naming a specific HTTP-verb-matching interface will be found only if the view is defined with a request_type that matches the HTTP verb in the incoming request. The more general ``IRequest`` interface can be used as the request_type to catch all requests (and this is indeed the default). All requests implement ``IRequest``. The HTTP-verb-matching idea was pioneered by `repoze.bfg.restrequest `_ . That package is no longer required, but still functions fine. Bug Fixes --------- - Fix a bug where the Paste configuration's ``unicode_path_segments`` (and os.environ's ``BFG_UNICODE_PATH_SEGMENTS``) may have been defaulting to false in some circumstances. It now always defaults to true, matching the documentation and intent. - The ``repoze.bfg.traversal.find_model`` API did not work properly when passed a ``path`` argument which was unicode and contained high-order bytes when the ``unicode_path_segments`` or ``BFG_UNICODE_PATH_SEGMENTS`` configuration variables were "true". - A new module was added: ``repoze.bfg.settings``. This contains deployment-settings-related code. Implementation Changes ---------------------- - The ``make_app`` callable within ``repoze.bfg.router`` now registers the ``root_policy`` argument as a utility (unnamed, using the new ``repoze.bfg.interfaces.IRootFactory`` as a provides interface) rather than passing it as the first argument to the ``repoze.bfg.router.Router`` class. As a result, the ``repoze.bfg.router.Router`` router class only accepts a single argument: ``registry``. The ``repoze.bfg.router.Router`` class retrieves the root policy via a utility lookup now. The ``repoze.bfg.router.make_app`` API also now performs some important application registrations that were previously handled inside ``repoze.bfg.registry.makeRegistry``. New Modules ----------- - A ``repoze.bfg.settings`` module was added. It contains code related to deployment settings. Most of the code it contains was moved to it from the ``repoze.bfg.registry`` module. Behavior Changes ---------------- - The ``repoze.bfg.settings.Settings`` class (an instance of which is registered as a utility providing ``repoze.bfg.interfaces.ISettings`` when any application is started) now automatically calls ``repoze.bfg.settings.get_options`` on the options passed to its constructor. This means that usage of ``get_options`` within an application's ``make_app`` function is no longer required (the "raw" ``options`` dict or None may be passed). - Remove old cold which attempts to recover from trying to unpickle a ``z3c.pt`` template; Chameleon has been the templating engine for a good long time now. Running repoze.bfg against a sandbox that has pickled ``z3c.pt`` templates it will now just fail with an unpickling error, but can be fixed by deleting the template cache files. Deprecations ------------ - Moved the ``repoze.bfg.registry.Settings`` class. This has been moved to ``repoze.bfg.settings.Settings``. A deprecation warning is issued when it is imported from the older location. - Moved the ``repoze.bfg.registry.get_options`` function This has been moved to ``repoze.bfg.settings.get_options``. A deprecation warning is issued when it is imported from the older location. - The ``repoze.bfg.interfaces.IRootPolicy`` interface was renamed within the interfaces package. It has been renamed to ``IRootFactory``. A deprecation warning is issued when it is imported from the older location. 0.6.1 (2009-01-06) ================== New Modules ----------- - A new module ``repoze.bfg.url`` has been added. It contains the ``model_url`` API (moved from ``repoze.bfg.traversal``) and an implementation of ``urlencode`` (like Python's ``urllib.urlencode``) which can handle Unicode keys and values in parameters to the ``query`` argument. Deprecations ------------ - The ``model_url`` function has been moved from ``repoze.bfg.traversal`` into ``repoze.bfg.url``. It can still be imported from ``repoze.bfg.traversal`` but an import from ``repoze.bfg.traversal`` will emit a DeprecationWarning. Features -------- - A ``static`` helper class was added to the ``repoze.bfg.views`` module. Instances of this class are willing to act as BFG views which return static resources using files on disk. See the ``repoze.bfg.view`` docs for more info. - The ``repoze.bfg.url.model_url`` API (nee' ``repoze.bfg.traversal.model_url``) now accepts and honors a keyword argument named ``query``. The value of this argument will be used to compose a query string, which will be attached to the generated URL before it is returned. See the API docs (in the docs directory or `on the web `_) for more information. 0.6 (2008-12-26) ================ Backwards Incompatibilities --------------------------- - Rather than prepare the "stock" implementations of the ZCML directives from the ``zope.configuration`` package for use under ``repoze.bfg``, ``repoze.bfg`` now makes available the implementations of directives from the ``repoze.zcml`` package (see http://static.repoze.org/zcmldocs). As a result, the ``repoze.bfg`` package now depends on the ``repoze.zcml`` package, and no longer depends directly on the ``zope.component``, ``zope.configuration``, ``zope.interface``, or ``zope.proxy`` packages. The primary reason for this change is to enable us to eventually reduce the number of inappropriate ``repoze.bfg`` Zope package dependencies, as well as to shed features of dependent package directives that don't make sense for ``repoze.bfg``. Note that currently the set of requirements necessary to use bfg has not changed. This is due to inappropriate Zope package requirements in ``chameleon.zpt``, which will hopefully be remedied soon. NOTE: in lemonade index a 1.0b8-repozezcml0 package exists which does away with these requirements. - BFG applications written prior to this release which expect the "stock" ``zope.component`` ZCML directive implementations (e.g. ``adapter``, ``subscriber``, or ``utility``) to function now must either 1) include the ``meta.zcml`` file from ``zope.component`` manually (e.g. ````) and include the ``zope.security`` package as an ``install_requires`` dependency or 2) change the ZCML in their applications to use the declarations from `repoze.zcml `_ instead of the stock declarations. ``repoze.zcml`` only makes available the ``adapter``, ``subscriber`` and ``utility`` directives. In short, if you've got an existing BFG application, after this update, if your application won't start due to an import error for "zope.security", the fastest way to get it working again is to add ``zope.security`` to the "install_requires" of your BFG application's ``setup.py``, then add the following ZCML anywhere in your application's ``configure.zcml``:: Then re-``setup.py develop`` or reinstall your application. - The ``http://namespaces.repoze.org/bfg`` XML namespace is now the default XML namespace in ZCML for paster-generated applications. The docs have been updated to reflect this. - The copies of BFG's ``meta.zcml`` and ``configure.zcml`` were removed from the root of the ``repoze.bfg`` package. In 0.3.6, a new package named ``repoze.bfg.includes`` was added, which contains the "correct" copies of these ZCML files; the ones that were removed were for backwards compatibility purposes. - The BFG ``view`` ZCML directive no longer calls ``zope.component.interface.provideInterface`` for the ``for`` interface. We don't support ``provideInterface`` in BFG because it mutates the global registry. Other ----- - The minimum requirement for ``chameleon.core`` is now 1.0b13. The minimum requirement for ``chameleon.zpt`` is now 1.0b8. The minimum requirement for ``chameleon.genshi`` is now 1.0b2. - Updated paster template "ez_setup.py" to one that requires setuptools 0.6c9. - Turn ``view_execution_permitted`` from the ``repoze.bfg.view`` module into a documented API. - Doc cleanups. - Documented how to create a view capable of serving static resources. 0.5.6 (2008-12-18) ================== - Speed up ``traversal.model_url`` execution by using a custom url quoting function instead of Python's ``urllib.quote``, by caching URL path segment quoting and encoding results, by disusing Python's ``urlparse.urljoin`` in favor of a simple string concatenation, and by using ``ob.__class__ is unicode`` rather than ``isinstance(ob, unicode)`` in one strategic place. 0.5.5 (2008-12-17) ================== Backwards Incompatibilities --------------------------- - In the past, during traversal, the ModelGraphTraverser (the default traverser) always passed each URL path segment to any ``__getitem__`` method of a model object as a byte string (a ``str`` object). Now, by default the ModelGraphTraverser attempts to decode the path segment to Unicode (a ``unicode`` object) using the UTF-8 encoding before passing it to the ``__getitem__`` method of a model object. This makes it possible for model objects to be dumber in ``__getitem__`` when trying to resolve a subobject, as model objects themselves no longer need to try to divine whether or not to try to decode the path segment passed by the traverser. Note that since 0.5.4, URLs generated by repoze.bfg's ``model_url`` API will contain UTF-8 encoded path segments as necessary, so any URL generated by BFG itself will be decodeable by the traverser. If another application generates URLs to a BFG application, to be resolved successully, it should generate the URL with UTF-8 encoded path segments to be successfully resolved. The decoder is not at all magical: if a non-UTF-8-decodeable path segment (e.g. one encoded using UTF-16 or some other insanity) is passed in the URL, BFG will raise a ``TypeError`` with a message indicating it could not decode the path segment. To turn on the older behavior, where path segments were not decoded to Unicode before being passed to model object ``__getitem__`` by the traverser, and were passed as a raw byte string, set the ``unicode_path_segments`` configuration setting to a false value in your BFG application's section of the paste .ini file, for example:: unicode_path_segments = False Or start the application using the ``BFG_UNICODE_PATH_SEGMENT`` envvar set to a false value:: BFG_UNICODE_PATH_SEGMENTS=0 0.5.4 (2008-12-13) ================== Backwards Incompatibilities --------------------------- - URL-quote "extra" element names passed in as ``**elements`` to the ``traversal.model_url`` API. If any of these names is a Unicode string, encode it to UTF-8 before URL-quoting. This is a slight backwards incompatibility that will impact you if you were already UTF-8 encoding or URL-quoting the values you passed in as ``elements`` to this API. Bugfixes -------- - UTF-8 encode each segment in the model path used to generate a URL before url-quoting it within the ``traversal.model_url`` API. This is a bugfix, as Unicode cannot always be successfully URL-quoted. Features -------- - Make it possible to run unit tests using a buildout-generated Python "interpreter". - Add ``request.root`` to ``router.Router`` in order to have easy access to the application root. 0.5.3 (2008-12-07) ================== - Remove the ``ITestingTemplateRenderer`` interface. When ``testing.registerDummyRenderer`` is used, it instead registers a dummy implementation using ``ITemplateRenderer`` interface, which is checked for when the built-in templating facilities do rendering. This change also allows developers to make explcit named utility registrations in the ZCML registry against ``ITemplateRenderer``; these will be found before any on-disk template is looked up. 0.5.2 (2008-12-05) ================== - The component registration handler for views (functions or class instances) now observes component adaptation annotations (see ``zope.component.adaptedBy``) and uses them before the fallback values for ``for_`` and ``request_type``. This change does not affect existing code insomuch as the code does not rely on these defaults when an annotation is set on the view (unlikely). This means that for a new-style class you can do ``zope.component.adapts(ISomeContext, ISomeRequest)`` at class scope or at module scope as a decorator to a bfg view function you can do ``@zope.component.adapter(ISomeContext, ISomeRequest)``. This differs from r.bfg.convention inasmuch as you still need to put something in ZCML for the registrations to get done; it's only the defaults that will change if these declarations exist. - Strip all slashes from end and beginning of path in clean_path within traversal machinery. 0.5.1 (2008-11-25) ================== - Add ``keys``, ``items``, and ``values`` methods to ``testing.DummyModel``. - Add __delitem__ method to ``testing.DummyModel``. 0.5.0 (2008-11-18) ================== - Fix ModelGraphTraverser; don't try to change the ``__name__`` or ``__parent__`` of an object that claims it implements ILocation during traversal even if the ``__name__`` or ``__parent__`` of the object traversed does not match the name used in the traversal step or the or the traversal parent . Rationale: it was insane to do so. This bug was only found due to a misconfiguration in an application that mistakenly had intermediate persistent non-ILocation objects; traversal was causing a persistent write on every request under this setup. - ``repoze.bfg.location.locate`` now unconditionally sets ``__name__`` and ``__parent__`` on objects which provide ILocation (it previously only set them conditionally if they didn't match attributes already present on the object via equality). 0.4.9 (2008-11-17) ================== - Add chameleon text template API (chameleon ${name} renderings where the template does not need to be wrapped in any containing XML). - Change docs to explain install in terms of a virtualenv (unconditionally). - Make pushpage decorator compatible with repoze.bfg.convention's ``bfg_view`` decorator when they're stacked. - Add content_length attribute to testing.DummyRequest. - Change paster template ``tests.py`` to include a true unit test. Retain old test as an integration test. Update documentation. - Document view registrations against classes and ``repoze.bfg.convention`` in context. - Change the default paster template to register its single view against a class rather than an interface. - Document adding a request type interface to the request via a subscriber function in the events narrative documentation. 0.4.8 (2008-11-12) ================== Backwards Incompatibilities --------------------------- - ``repoze.bfg.traversal.model_url`` now always appends a slash to all generated URLs unless further elements are passed in as the third and following arguments. Rationale: views often use ``model_url`` without the third-and-following arguments in order to generate a URL for a model in order to point at the default view of a model. The URL that points to the default view of the *root* model is technically ``http://mysite/`` as opposed to ``http://mysite`` (browsers happen to ask for '/' implicitly in the GET request). Because URLs are never automatically generated for anything *except* models by ``model_url``, and because the root model is not really special, we continue this pattern. The impact of this change is minimal (at most you will have too many slashes in your URL, which BFG deals with gracefully anyway). 0.4.7 (2008-11-11) ================== Features -------- - Allow ``testing.registerEventListener`` to be used with Zope 3 style "object events" (subscribers accept more than a single event argument). We extend the list with the arguments, rather than append. 0.4.6 (2008-11-10) ================== Bug Fixes --------- - The ``model_path`` and ``model_url`` traversal APIs returned the wrong value for the root object (e.g. ``model_path`` returned ``''`` for the root object, while it should have been returning ``'/'``). 0.4.5 (2008-11-09) ================== Features -------- - Added a ``clone`` method and a ``__contains__`` method to the DummyModel testing object. - Allow DummyModel objects to receive extra keyword arguments, which will be attached as attributes. - The DummyTemplateRenderer now returns ``self`` as its implementation. 0.4.4 (2008-11-08) ================== Features -------- - Added a ``repoze.bfg.testing`` module to attempt to make it slightly easier to write unittest-based automated tests of BFG applications. Information about this module is in the documentation. - The default template renderer now supports testing better by looking for ``ITestingTemplateRenderer`` using a relative pathname. This is exposed indirectly through the API named ``registerTemplateRenderer`` in ``repoze.bfg.testing``. Deprecations ------------ - The names ``repoze.bfg.interfaces.ITemplate`` , ``repoze.bfg.interfaces.ITemplateFactory`` and ``repoze.bfg.interfaces.INodeTemplate`` have been deprecated. These should now be imported as ``repoze.bfg.interfaces.ITemplateRenderer`` and ``repoze.bfg.interfaces.ITemplateRendererFactory``, and ``INodeTemplateRenderer`` respectively. - The name ``repoze.bfg.chameleon_zpt.ZPTTemplateFactory`` is deprecated. Use ``repoze.bfg.chameleon_zpt.ZPTTemplateRenderer``. - The name ``repoze.bfg.chameleon_genshi.GenshiTemplateFactory`` is deprecated. Use ``repoze.bfg.chameleon_genshi.GenshiTemplateRenderer``. - The name ``repoze.bfg.xslt.XSLTemplateFactory`` is deprecated. Use ``repoze.bfg.xslt.XSLTemplateRenderer``. 0.4.3 (2008-11-02) ================== Bug Fixes --------- - Not passing the result of "get_options" as the second argument of make_app could cause attribute errors when attempting to look up settings against the ISettings object (internal). Fixed by giving the Settings objects defaults for ``debug_authorization`` and ``debug_notfound``. - Return an instance of ``Allowed`` (rather than ``True``) from ``has_permission`` when no security policy is in use. - Fix bug where default deny in authorization check would throw a TypeError (use ``ACLDenied`` instead of ``Denied``). 0.4.2 (2008-11-02) ================== Features -------- - Expose a single ILogger named "repoze.bfg.debug" as a utility; this logger is registered unconditionally and is used by the authorization debug machinery. Applications may also make use of it as necessary rather than inventing their own logger, for convenience. - The ``BFG_DEBUG_AUTHORIZATION`` envvar and the ``debug_authorization`` config file value now only imply debugging of view-invoked security checks. Previously, information was printed for every call to ``has_permission`` as well, which made output confusing. To debug ``has_permission`` checks and other manual permission checks, use the debugger and print statements in your own code. - Authorization debugging info is now only present in the HTTP response body oif ``debug_authorization`` is true. - The format of authorization debug messages was improved. - A new ``BFG_DEBUG_NOTFOUND`` envvar was added and a symmetric ``debug_notfound`` config file value was added. When either is true, and a NotFound response is returned by the BFG router (because a view could not be found), debugging information is printed to stderr. When this value is set true, the body of HTTPNotFound responses will also contain the same debugging information. - ``Allowed`` and ``Denied`` responses from the security machinery are now specialized into two types: ACL types, and non-ACL types. The ACL-related responses are instances of ``repoze.bfg.security.ACLAllowed`` and ``repoze.bfg.security.ACLDenied``. The non-ACL-related responses are ``repoze.bfg.security.Allowed`` and ``repoze.bfg.security.Denied``. The allowed-type responses continue to evaluate equal to things that themselves evaluate equal to the ``True`` boolean, while the denied-type responses continue to evaluate equal to things that themselves evaluate equal to the ``False`` boolean. The only difference between the two types is the information attached to them for debugging purposes. - Added a new ``BFG_DEBUG_ALL`` envvar and a symmetric ``debug_all`` config file value. When either is true, all other debug-related flags are set true unconditionally (e.g. ``debug_notfound`` and ``debug_authorization``). Documentation ------------- - Added info about debug flag changes. - Added a section to the security chapter named "Debugging Imperative Authorization Failures" (for e.g. ``has_permssion``). Bug Fixes --------- - Change default paster template generator to use ``Paste#http`` server rather than ``PasteScript#cherrpy`` server. The cherrypy server has a security risk in it when ``REMOTE_USER`` is trusted by the downstream application. 0.4.1 (2008-10-28) ================== Bug Fixes --------- - If the ``render_view_to_response`` function was called, if the view was found and called, but it returned something that did not implement IResponse, the error would pass by unflagged. This was noticed when I created a view function that essentially returned None, but received a NotFound error rather than a ValueError when the view was rendered. This was fixed. 0.4.0 (2008-10-03) ================== Docs ---- - An "Environment and Configuration" chapter was added to the narrative portion of the documentation. Features -------- - Ensure bfg doesn't generate warnings when running under Python 2.6. - The environment variable ``BFG_RELOAD_TEMPLATES`` is now available (serves the same purpose as ``reload_templates`` in the config file). - A new configuration file option ``debug_authorization`` was added. This turns on printing of security authorization debug statements to ``sys.stderr``. The ``BFG_DEBUG_AUTHORIZATION`` environment variable was also added; this performs the same duty. Bug Fixes --------- - The environment variable ``BFG_SECURITY_DEBUG`` did not always work. It has been renamed to ``BFG_DEBUG_AUTHORIZATION`` and fixed. Deprecations ------------ - A deprecation warning is now issued when old API names from the ``repoze.bfg.templates`` module are imported. Backwards incompatibilities --------------------------- - The ``BFG_SECURITY_DEBUG`` environment variable was renamed to ``BFG_DEBUG_AUTHORIZATION``. 0.3.9 (2008-08-27) ================== Features -------- - A ``repoze.bfg.location`` API module was added. Backwards incompatibilities --------------------------- - Applications must now use the ``repoze.bfg.interfaces.ILocation`` interface rather than ``zope.location.interfaces.ILocation`` to represent that a model object is "location-aware". We've removed a dependency on ``zope.location`` for cleanliness purposes: as new versions of zope libraries are released which have improved dependency information, getting rid of our dependence on ``zope.location`` will prevent a newly installed repoze.bfg application from requiring the ``zope.security``, egg, which not truly used at all in a "stock" repoze.bfg setup. These dependencies are still required by the stack at this time; this is purely a futureproofing move. The security and model documentation for previous versions of ``repoze.bfg`` recommended using the ``zope.location.interfaces.ILocation`` interface to represent that a model object is "location-aware". This documentation has been changed to reflect that this interface should now be imported from ``repoze.bfg.interfaces.ILocation`` instead. 0.3.8 (2008-08-26) ================== Docs ---- - Documented URL dispatch better in narrative form. Bug fixes --------- - Routes URL dispatch did not have access to the WSGI environment, so conditions such as method=GET did not work. Features -------- - Add ``principals_allowed_by_permission`` API to security module. - Replace ``z3c.pt`` support with support for ``chameleon.zpt``. Chameleon is the new name for the package that used to be named ``z3c.pt``. NOTE: If you update a ``repoze.bfg`` SVN checkout that you're using for development, you will need to run "setup.py install" or "setup.py develop" again in order to obtain the proper Chameleon packages. ``z3c.pt`` is no longer supported by ``repoze.bfg``. All API functions that used to render ``z3c.pt`` templates will work fine with the new packages, and your templates should render almost identically. - Add a ``repoze.bfg.chameleon_zpt`` module. This module provides Chameleon ZPT support. - Add a ``repoze.bfg.xslt`` module. This module provides XSLT support. - Add a ``repoze.bfg.chameleon_genshi`` module. This provides direct Genshi support, which did not exist previously. Deprecations ------------ - Importing API functions directly from ``repoze.bfg.template`` is now deprecated. The ``get_template``, ``render_template``, ``render_template_to_response`` functions should now be imported from ``repoze.chameleon_zpt``. The ``render_transform``, and ``render_transform_to_response`` functions should now be imported from ``repoze.bfg.xslt``. The ``repoze.bfg.template`` module will remain around "forever" to support backwards compatibility. 0.3.7 (2008-09-09) ================== Features -------- - Add compatibility with z3c.pt 1.0a7+ (z3c.pt became a namespace package). Bug fixes --------- - ``repoze.bfg.traversal.find_model`` function did not function properly. 0.3.6 (2008-09-04) ================== Features -------- - Add startup process docs. - Allow configuration cache to be bypassed by actions which include special "uncacheable" discriminators (for actions that have variable results). Bug Fixes --------- - Move core repoze.bfg ZCML into a ``repoze.bfg.includes`` package so we can use repoze.bfg better as a namespace package. Adjust the code generator to use it. We've left around the ``configure.zcml`` in the repoze.bfg package directly so as not to break older apps. - When a zcml application registry cache was unpickled, and it contained a reference to an object that no longer existed (such as a view), bfg would not start properly. 0.3.5 (2008-09-01) ================== Features -------- - Event notification is issued after application is created and configured (``IWSGIApplicationCreatedEvent``). - New API module: ``repoze.bfg.view``. This module contains the functions named ``render_view_to_response``, ``render_view_to_iterable``, ``render_view`` and ``is_response``, which are documented in the API docs. These features aid programmatic (non-server-driven) view execution. 0.3.4 (2008-08-28) ================== Backwards incompatibilities --------------------------- - Make ``repoze.bfg`` a namespace package so we can allow folks to create subpackages (e.g. ``repoze.bfg.otherthing``) within separate eggs. This is a backwards incompatible change which makes it impossible to import "make_app" and "get_options" from the ``repoze.bfg`` module directly. This change will break all existing apps generated by the paster code generator. Instead, you need to import these functions as ``repoze.bfg.router:make_app`` and ``repoze.bfg.registry:get_options``, respectively. Sorry folks, it has to be done now or never, and definitely better now. Features -------- - Add ``model_path`` API function to traversal module. Bugfixes - Normalize path returned by repoze.bfg.caller_path. 0.3.3 (2008-08-23) ================== - Fix generated test.py module to use project name rather than package name. 0.3.2 (2008-08-23) ================== - Remove ``sampleapp`` sample application from bfg package itself. - Remove dependency on FormEncode (only needed by sampleapp). - Fix paster template generation so that case-sensitivity is preserved for project vs. package name. - Depend on ``z3c.pt`` version 1.0a1 (which requires the ``[lxml]`` extra currently). - Read and write a pickled ZCML actions list, stored as ``configure.zcml.cache`` next to the applications's "normal" configuration file. A given bfg app will usually start faster if it's able to read the pickle data. It fails gracefully to reading the real ZCML file if it cannot read the pickle. 0.3.1 (2008-08-20) ================== - Generated application differences: ``make_app`` entry point renamed to ``app`` in order to have a different name than the bfg function of the same name, to prevent confusion. - Add "options" processing to bfg's ``make_app`` to support runtime options. A new API function named ``get_options`` was added to the registry module. This function is typically used in an application's ``app`` entry point. The Paste config file section for the app can now supply the ``reload_templates`` option, which, if true, will prevent the need to restart the appserver in order for ``z3c.pt`` or XSLT template changes to be detected. - Use only the module name in generated project's "test_suite" (run all tests found in the package). - Default port for generated apps changed from 5432 to 6543 (Postgres default port is 6543). 0.3.0 (2008-08-16) ================== - Add ``get_template`` API to template module. 0.2.9 (2008-08-11) ================== - 0.2.8 was "brown bag" release. It didn't work at all. Symptom: ComponentLookupError when trying to render a page. 0.2.8 (2008-08-11) ================== - Add ``find_model`` and ``find_root`` traversal APIs. In the process, make ITraverser a uni-adapter (on context) rather than a multiadapter (on context and request). 0.2.7 (2008-08-05) ================== - Add a ``request_type`` attribute to the available attributes of a ``bfg:view`` configure.zcml element. This attribute will have a value which is a dotted Python path, pointing at an interface. If the request object implements this interface when the view lookup is performed, the appropriate view will be called. This is meant to allow for simple "skinning" of sites based on request type. An event subscriber should attach the interface to the request on ingress to support skins. - Remove "template only" views. These were just confusing and were never documented. - Small url dispatch overhaul: the ``connect`` method of the ``urldispatch.RoutesMapper`` object now accepts a keyword parameter named ``context_factory``. If this parameter is supplied, it must be a callable which returns an instance. This instance is used as the context for the request when a route is matched. - The registration of a RoutesModelTraverser no longer needs to be performed by the application; it's in the bfg ZCML now. 0.2.6 (2008-07-31) ================== - Add event sends for INewRequest and INewResponse. See the events.rst chapter in the documentation's ``api`` directory. 0.2.5 (2008-07-28) ================== - Add ``model_url`` API. 0.2.4 (2008-07-27) ================== - Added url-based dispatch. 0.2.3 (2008-07-20) ================== - Add API functions for authenticated_userid and effective_principals. 0.2.2 (2008-07-20) ================== - Add authenticated_userid and effective_principals API to security policy. 0.2.1 (2008-07-20) ================== - Add find_interface API. 0.2 (2008-07-19) ================ - Add wsgiapp decorator. - The concept of "view factories" was removed in favor of always calling a view, which is a callable that returns a response directly (as opposed to returning a view). As a result, the ``factory`` attribute in the bfg:view ZCML statement has been renamed to ``view``. Various interface names were changed also. - ``render_template`` and ``render_transform`` no longer return a Response object. Instead, these return strings. The old behavior can be obtained by using ``render_template_to_response`` and ``render_transform_to_response``. - Added 'repoze.bfg.push:pushpage' decorator, which creates BFG views from callables which take (context, request) and return a mapping of top-level names. - Added ACL-based security. - Support for XSLT templates via a render_transform method 0.1 (2008-07-08) ================ - Initial release. pyramid-1.4.5/PKG-INFO0000664000175000017500000012162012210157153013556 0ustar takakitakakiMetadata-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/HACKING.txt0000664000175000017500000001257312210154276014277 0ustar takakitakakiHacking on Pyramid ================== Here are some guidelines about hacking on Pyramid. Using a Development Checkout ---------------------------- Below is a quick start on creating a development environment using a Pyramid checkout. - Create a new directory somewhere and ``cd`` to it:: $ mkdir ~/hack-on-pyramid $ cd ~/hack-on-pyramid - Check out a read-only copy of the Pyramid source:: $ git clone git://github.com/Pylons/pyramid.git (alternately, create a writeable fork on GitHub and check that out). - Create a virtualenv in which to install Pyramid:: $ virtualenv2.6 --no-site-packages env - Install ``setuptools-git`` into the virtualenv (for good measure, as we're using git to do version control):: $ env/bin/easy_install setuptools-git - Install Pyramid from the checkout into the virtualenv using ``setup.py dev``. ``setup.py dev`` is an alias for "setup.py develop" which also installs testing requirements such as nose and coverage. Running ``setup.py dev`` *must* be done while the current working directory is the ``pyramid`` checkout directory:: $ cd pyramid $ ../env/bin/python setup.py dev - At that point, you should be able to create new Pyramid projects by using ``pcreate``:: $ cd ../env $ bin/pcreate -s starter starter - And install those projects (also using ``setup.py develop``) into the virtualenv:: $ cd starter $ ../bin/python setup.py develop Adding Features --------------- In order to add a feature to Pyramid: - The feature must be documented in both the API and narrative documentation (in ``docs/``). - The feature must work fully on the following CPython versions: 2.6, 2.7, and 3.2 on both UNIX and Windows. - The feature must work on the latest version of PyPy. - The feature must not cause installation or runtime failure on App Engine. If it doesn't cause installation or runtime failure, but doesn't actually *work* on these platforms, that caveat should be spelled out in the documentation. - The feature must not depend on any particular persistence layer (filesystem, SQL, etc). - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should be discussed). The above requirements are relaxed for scaffolding dependencies. If a scaffold has an install-time dependency on something that doesn't work on a particular platform, that caveat should be spelled out clearly in *its* documentation (within its ``docs/`` directory). Coding Style ------------ - PEP8 compliance. Whitespace rules are relaxed: not necessary to put 2 newlines between classes. But 80-column lines, in particular, are mandatory. - Please do not remove trailing whitespace. Configure your editor to reduce diff noise. Running Tests -------------- - To run tests for Pyramid on a single Python version, run ``python setup.py test`` against the using the Python interpreter from virtualenv into which you've ``setup.py develop``-ed Pyramid. - To run the full set of Pyramid tests on all platforms, install ``tox`` (http://codespeak.net/~hpk/tox/) into a system Python. The ``tox`` console script will be installed into the scripts location for that Python. While ``cd``'ed to the Pyramid checkout root directory (it contains ``tox.ini``), invoke the ``tox`` console script. This will read the ``tox.ini`` file and execute the tests on multiple Python versions and platforms; while it runs, it creates a virtualenv for each version/platform combination. For example:: $ /usr/bin/easy_install tox $ cd ~/hack-on-pyramid/pyramid $ /usr/bin/tox Test Coverage ------------- - The codebase *must* have 100% test statement coverage after each commit. You can test coverage via ``tox -e coverage``, or alternately by installing ``nose`` and ``coverage`` into your virtualenv (easiest via ``setup.py dev``) , and running ``setup.py nosetests --with-coverage``. Documentation Coverage and Building HTML Documentation ------------------------------------------------------ If you fix a bug, and the bug requires an API or behavior modification, all documentation in this package which references that API or behavior must change to reflect the bug fix, ideally in the same commit that fixes the bug or adds the feature. To build and review docs (where ``$yourvenv`` refers to the virtualenv you're using to develop Pyramid): 1. Run ``$yourvenv/bin/python setup.py dev docs``. This will cause Sphinx and all development requirements to be installed in your virtualenv. 2. Update all git submodules from the top-level of your Pyramid checkout, like so: git submodule update --init --recursive This will checkout theme subrepositories and prevent error conditions when HTML docs are generated. 3. cd to the ``docs`` directory within your Pyramid checkout and execute ``make clean html SPHINXBUILD=$yourvenv/bin/sphinx-build``. The ``SPHINXBUILD=...`` hair is there in order to tell it to use the virtualenv Python, which will have both Sphinx and Pyramid (for API documentation generation) installed. 4. Open the ``docs/_build/html/index.html`` file to see the resulting HTML rendering. Change Log ---------- - Feature additions and bugfixes must be added to the ``CHANGES.txt`` file in the prevailing style. Changelog entries should be long and descriptive, not cryptic. Other developers should be able to know what your changelog entry means. pyramid-1.4.5/CONTRIBUTORS.txt0000664000175000017500000001416312210154276015165 0ustar takakitakakiPylons Project Contributor Agreement ==================================== The submitter agrees by adding his or her name within the section below named "Contributors" and submitting the resulting modified document to the canonical shared repository location for this software project (whether directly, as a user with "direct commit access", or via a "pull request"), he or she is signing a contract electronically. The submitter becomes a Contributor after a) he or she signs this document by adding their name beneath the "Contributors" section below, and b) the resulting document is accepted into the canonical version control repository. Treatment of Account --------------------- Contributor will not allow anyone other than the Contributor to use his or her username or source repository login to submit code to a Pylons Project source repository. Should Contributor become aware of any such use, Contributor will immediately notify Agendaless Consulting. Notification must be performed by sending an email to webmaster@agendaless.com. Until such notice is received, Contributor will be presumed to have taken all actions made through Contributor's account. If the Contributor has direct commit access, Agendaless Consulting will have complete control and discretion over capabilities assigned to Contributor's account, and may disable Contributor's account for any reason at any time. Legal Effect of Contribution ---------------------------- Upon submitting a change or new work to a Pylons Project source Repository (a "Contribution"), you agree to assign, and hereby do assign, a one-half interest of all right, title and interest in and to copyright and other intellectual property rights with respect to your new and original portions of the Contribution to Agendaless Consulting. You and Agendaless Consulting each agree that the other shall be free to exercise any and all exclusive rights in and to the Contribution, without accounting to one another, including without limitation, the right to license the Contribution to others under the Repoze Public License. This agreement shall run with title to the Contribution. Agendaless Consulting does not convey to you any right, title or interest in or to the Program or such portions of the Contribution that were taken from the Program. Your transmission of a submission to the Pylons Project source Repository and marks of identification concerning the Contribution itself constitute your intent to contribute and your assignment of the work in accordance with the provisions of this Agreement. License Terms ------------- Code committed to the Pylons Project source repository (Committed Code) must be governed by the Repoze Public License (http://repoze.org/LICENSE.txt, aka "the RPL") or another license acceptable to Agendaless Consulting. Until Agendaless Consulting declares in writing an acceptable license other than the RPL, only the RPL shall be used. A list of exceptions is detailed within the "Licensing Exceptions" section of this document, if one exists. Representations, Warranty, and Indemnification ---------------------------------------------- Contributor represents and warrants that the Committed Code does not violate the rights of any person or entity, and that the Contributor has legal authority to enter into this Agreement and legal authority over Contributed Code. Further, Contributor indemnifies Agendaless Consulting against violations. Cryptography ------------ Contributor understands that cryptographic code may be subject to government regulations with which Agendaless Consulting and/or entities using Committed Code must comply. Any code which contains any of the items listed below must not be checked-in until Agendaless Consulting staff has been notified and has approved such contribution in writing. - Cryptographic capabilities or features - Calls to cryptographic features - User interface elements which provide context relating to cryptography - Code which may, under casual inspection, appear to be cryptographic. Notices ------- Contributor confirms that any notices required will be included in any Committed Code. Licensing Exceptions ==================== Code committed within the ``docs/`` subdirectory of the Pyramid source control repository and "docstrings" which appear in the documentation generated by running "make" within this directory is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License (http://creativecommons.org/licenses/by-nc-sa/3.0/us/). List of Contributors ==================== The below-signed are contributors to a code repository that is part of the project named "Pyramid". Each below-signed contributor has read, understand and agrees to the terms above in the section within this document entitled "Pylons Project Contributor Agreement" as of the date beside his or her name. Contributors ------------ - Chris McDonough, 2010/11/08 - Tres Seaver, 2010/11/09 - Ben Bangert, 2010/11/09 - Blaise Laflamme, 2010/11/09 - Chris Rossi, 2010/11/10 - Casey Duncan, 2010/12/27 - Rob Miller, 2010/12/28 - Marius Gedminas, 2010/12/31 - Marcin Lulek, 2011/01/02 - John Shipman, 2011/01/15 - Wichert Akkerman, 2011/01/19 - Christopher Lambacher, 2011/02/12 - Malthe Borch, 2011/02/28 - Carlos de la Guardia, 2011/03/29 - Joel Bohman, 2011/04/16 - Juliusz Gonera, 2011/04/17 - Philip Jenvey, 2011/04/24 - Michael Merickel, 2011/5/25 - Christoph Zwerschke, 2011/06/07 - Atsushi Odagiri, 2011/07/02 - Shane Hathaway, 2011/07/22 - Manuel Hermann, 2011/07/11 - Richard Barrell, 2011/11/07 - Chris Shenton, 2011/11/07 - Ken Manheimer, 2011/11/07 - Reed O'Brien, 2011/11/07 - Klee Dienes, 2011/10/30 - Michael Ryabushin, 2011/12/14 - Mike Orr, 2012/02/14 - Paul M. Winkler, 2012/02/22 - Martijn Pieters, 2012/03/02 - Steve Piercy, 2012/03/27 - Wayne Witzel III, 2012/03/27 - Marin Rukavina, 2012/05/03 - Lorenzo M. Catucci, 2012/06/08 - Marc Abramowitz, 2012/06/13 - Brian Sutherland, 2012/06/16 - Jeff Cook, 2012/06/16 - Ian Wilson, 2012/06/17 - Roman Kozlovskyi, 2012/08/11 - Domen Kozar, 2012/09/11 - David Gay, 2012/09/16 - Robert Jackiewicz, 2012/11/12 - John Anderson, 2012/11/14 - Jason McKellar, 2013/03/28 pyramid-1.4.5/LICENSE.txt0000664000175000017500000001632712203712502014310 0ustar takakitakakiThe majority of the code in Pyramid is supplied under this license: A copyright notice accompanies this license document that identifies the copyright holders. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Portions of the code in Pyramid are supplied under the ZPL (headers within individiual files indicate that these portions are licensed under the ZPL): Zope Public License (ZPL) Version 2.1 ------------------------------------- A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The documentation portion of Pyramid (the rendered contents of the "docs" directory of a software distribution or checkout) is supplied under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License as described by http://creativecommons.org/licenses/by-nc-sa/3.0/us/ Internationalization Code in ``pyramid.i18n`` is supplied under the following license: Copyright (C) 2007 Edgewall Software All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Portions of the code marked as "stolen from Paste" are provided under the following license: Copyright (c) 2006-2007 Ian Bicking and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.