Merge branch 'master' of https://github.com/Pylons/pyramid into start_pep8
Conflicts:
pyramid/config/views.py
pyramid/scaffolds/tests.py
tox.ini
3 files deleted
1 files added
49 files modified
| | |
| | | *.egg |
| | | *.egg-info |
| | | .eggs/ |
| | | *.pyc |
| | | *$py.class |
| | | *.pt.py |
| | |
| | | *~ |
| | | .*.swp |
| | | .coverage |
| | | .coverage.* |
| | | .tox/ |
| | | nosetests.xml |
| | | coverage.xml |
| | | nosetests-*.xml |
| | | coverage-*.xml |
| | | tutorial.db |
| | | build/ |
| | | dist/ |
| | |
| | | - TOXENV=py34 |
| | | - TOXENV=pypy |
| | | - TOXENV=pypy3 |
| | | - TOXENV=cover |
| | | - TOXENV=py2-docs |
| | | - TOXENV=py3-docs |
| | | - TOXENV=py2-cover,py3-cover,coverage |
| | | |
| | | install: |
| | | - travis_retry pip install tox |
| | |
| | | Features |
| | | -------- |
| | | |
| | | - The ``pyramid.config.Configurator`` has grown the ability to allow |
| | | actions to call other actions during a commit-cycle. This enables much more |
| | | logic to be placed into actions, such as the ability to invoke other actions |
| | | or group them for improved conflict detection. We have also exposed and |
| | | documented the config phases that Pyramid uses in order to further assist |
| | | in building conforming addons. |
| | | See https://github.com/Pylons/pyramid/pull/1513 |
| | | |
| | | - Add ``pyramid.request.apply_request_extensions`` function which can be |
| | | used in testing to apply any request extensions configured via |
| | | ``config.add_request_method``. Previously it was only possible to test |
| | | the extensions by going through Pyramid's router. |
| | | See https://github.com/Pylons/pyramid/pull/1581 |
| | | |
| | | - pcreate when run without a scaffold argument will now print information on |
| | | the missing flag, as well as a list of available scaffolds. |
| | | See https://github.com/Pylons/pyramid/pull/1566 and |
| | |
| | | - Added support / testing for 'pypy3' under Tox and Travis. |
| | | See https://github.com/Pylons/pyramid/pull/1469 |
| | | |
| | | - Automate code coverage metrics across py2 and py3 instead of just py2. |
| | | See https://github.com/Pylons/pyramid/pull/1471 |
| | | |
| | | - Cache busting for static resources has been added and is available via a new |
| | | argument to ``pyramid.config.Configurator.add_static_view``: ``cachebust``. |
| | | See https://github.com/Pylons/pyramid/pull/1380 |
| | | Core APIs are shipped for both cache busting via query strings and |
| | | path segments and may be extended to fit into custom asset pipelines. |
| | | See https://github.com/Pylons/pyramid/pull/1380 and |
| | | https://github.com/Pylons/pyramid/pull/1583 |
| | | |
| | | - Add ``pyramid.config.Configurator.root_package`` attribute and init |
| | | parameter to assist with includeable packages that wish to resolve |
| | |
| | | - Support keyword-only arguments and function annotations in views in |
| | | Python 3. See https://github.com/Pylons/pyramid/pull/1556 |
| | | |
| | | - ``request.response`` will no longer be mutated when using the |
| | | ``pyramid.renderers.render_to_response()`` API. It is now necessary to |
| | | pass in a ``response=`` argument to ``render_to_response`` if you wish to |
| | | supply the renderer with a custom response object for it to use. If you |
| | | do not pass one then a response object will be created using the |
| | | application's ``IResponseFactory``. Almost all renderers |
| | | mutate the ``request.response`` response object (for example, the JSON |
| | | renderer sets ``request.response.content_type`` to ``application/json``). |
| | | However, when invoking ``render_to_response`` it is not expected that the |
| | | response object being returned would be the same one used later in the |
| | | request. The response object returned from ``render_to_response`` is now |
| | | explicitly different from ``request.response``. This does not change the |
| | | API of a renderer. See https://github.com/Pylons/pyramid/pull/1563 |
| | | |
| | | Bug Fixes |
| | | --------- |
| | | |
| | | - Work around an issue where ``pserve --reload`` would leave terminal echo |
| | | disabled if it reloaded during a pdb session. |
| | | See https://github.com/Pylons/pyramid/pull/1577, |
| | | https://github.com/Pylons/pyramid/pull/1592 |
| | | |
| | | - ``pyramid.wsgi.wsgiapp`` and ``pyramid.wsgi.wsgiapp2`` now raise |
| | | ``ValueError`` when accidentally passed ``None``. |
| | |
| | | |
| | | - Allow the ``pyramid.renderers.JSONP`` renderer to work even if there is no |
| | | valid request object. In this case it will not wrap the object in a |
| | | callback and thus behave just like the ``pyramid.renderers.JSON` renderer. |
| | | callback and thus behave just like the ``pyramid.renderers.JSON`` renderer. |
| | | See https://github.com/Pylons/pyramid/pull/1561 |
| | | |
| | | - Prevent "parameters to load are deprecated" ``DeprecationWarning`` |
| | | from setuptools>=11.3. See https://github.com/Pylons/pyramid/pull/1541 |
| | | |
| | | - Avoiding sharing the ``IRenderer`` objects across threads when attached to |
| | | a view using the `renderer=` argument. These renderers were instantiated |
| | | at time of first render and shared between requests, causing potentially |
| | | subtle effects like `pyramid.reload_templates = true` failing to work |
| | | in `pyramid_mako`. See https://github.com/Pylons/pyramid/pull/1575 |
| | | and https://github.com/Pylons/pyramid/issues/1268 |
| | | |
| | | - Avoiding timing attacks against CSRF tokens. |
| | | See https://github.com/Pylons/pyramid/pull/1574 |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | |
| | | - Ilja Everila, 2015/02/05 |
| | | |
| | | - Geoffrey T. Dairiki, 2015/02/06 |
| | | |
| | | - David Glick, 2015/02/12 |
| | |
| | | ------------- |
| | | |
| | | - The codebase *must* have 100% test statement coverage after each commit. |
| | | You can test coverage via ``tox -e coverage``, or alternately by installing |
| | | You can test coverage via ``tox -e cover``, or alternately by installing |
| | | ``nose`` and ``coverage`` into your virtualenv (easiest via ``setup.py |
| | | dev``) , and running ``setup.py nosetests --with-coverage``. |
| | | |
| | |
| | | are being used. |
| | | |
| | | .. autoclass:: not_ |
| | | |
| | | .. attribute:: PHASE0_CONFIG |
| | | .. attribute:: PHASE1_CONFIG |
| | | .. attribute:: PHASE2_CONFIG |
| | | .. attribute:: PHASE3_CONFIG |
| | |
| | | that used as ``request.GET``, ``request.POST``, and ``request.params``), |
| | | see :class:`pyramid.interfaces.IMultiDict`. |
| | | |
| | | .. autofunction:: apply_request_extensions(request) |
| | |
| | | :members: |
| | | :inherited-members: |
| | | |
| | | .. autoclass:: PathSegmentCacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringCacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: PathSegmentMd5CacheBuster |
| | | :members: |
| | | |
| | |
| | | scratch which implements the :class:`~pyramid.interfaces.ICacheBuster` |
| | | interface. Alternatively you may choose to subclass one of the existing |
| | | implementations. One of the most likely scenarios is you'd want to change the |
| | | way the asset token is generated. To do this just subclass an existing |
| | | implementation and replace the :meth:`~pyramid.interfaces.ICacheBuster.token` |
| | | method. Here is an example which just uses Git to get the hash of the |
| | | currently checked out code: |
| | | way the asset token is generated. To do this just subclass either |
| | | :class:`~pyramid.static.PathSegmentCacheBuster` or |
| | | :class:`~pyramid.static.QueryStringCacheBuster` and define a |
| | | ``tokenize(pathspec)`` method. Here is an example which just uses Git to get |
| | | the hash of the currently checked out code: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | import os |
| | | import subprocess |
| | | from pyramid.static import PathSegmentMd5CacheBuster |
| | | from pyramid.static import PathSegmentCacheBuster |
| | | |
| | | class GitCacheBuster(PathSegmentMd5CacheBuster): |
| | | class GitCacheBuster(PathSegmentCacheBuster): |
| | | """ |
| | | Assuming your code is installed as a Git checkout, as opposed to as an |
| | | egg from an egg repository like PYPI, you can use this cachebuster to |
| | |
| | | ['git', 'rev-parse', 'HEAD'], |
| | | cwd=here).strip() |
| | | |
| | | def token(self, pathspec): |
| | | def tokenize(self, pathspec): |
| | | return self.sha1 |
| | | |
| | | Choosing a Cache Buster |
| | |
| | | passed to it, that a route by this name was already registered by |
| | | ``add_route``, and if such a route has not already been registered, it's a |
| | | configuration error (a view that names a nonexistent route via its |
| | | ``route_name`` parameter will never be called). |
| | | ``route_name`` parameter will never be called). As of Pyramid 1.6 it is |
| | | possible for one action to invoke another. See :ref:`ordering_actions` for |
| | | more information. |
| | | |
| | | ``introspectables`` is a sequence of :term:`introspectable` objects. You can |
| | | pass a sequence of introspectables to the |
| | | :meth:`~pyramid.config.Configurator.action` method, which allows you to |
| | | augment Pyramid's configuration introspection system. |
| | | |
| | | .. _ordering_actions: |
| | | |
| | | Ordering Actions |
| | | ---------------- |
| | | |
| | | In Pyramid every :term:`action` has an inherent ordering relative to other |
| | | actions. The logic within actions is deferred until a call to |
| | | :meth:`pyramid.config.Configurator.commit` (which is automatically invoked by |
| | | :meth:`pyramid.config.Configurator.make_wsgi_app`). This means you may call |
| | | ``config.add_view(route_name='foo')`` **before** |
| | | ``config.add_route('foo', '/foo')`` because nothing actually happens until |
| | | commit-time. During a commit cycle conflicts are resolved, actions are ordered |
| | | and executed. |
| | | |
| | | By default, almost every action in Pyramid has an ``order`` of |
| | | :const:`pyramid.config.PHASE3_CONFIG`. Every action within the same order-level |
| | | will be executed in the order it was called. |
| | | This means that if an action must be reliably executed before or after another |
| | | action, the ``order`` must be defined explicitly to make this work. For |
| | | example, views are dependent on routes being defined. Thus the action created |
| | | by :meth:`pyramid.config.Configurator.add_route` has an ``order`` of |
| | | :const:`pyramid.config.PHASE2_CONFIG`. |
| | | |
| | | Pre-defined Phases |
| | | ~~~~~~~~~~~~~~~~~~ |
| | | |
| | | :const:`pyramid.config.PHASE0_CONFIG` |
| | | |
| | | - This phase is reserved for developers who want to execute actions prior |
| | | to Pyramid's core directives. |
| | | |
| | | :const:`pyramid.config.PHASE1_CONFIG` |
| | | |
| | | - :meth:`pyramid.config.Configurator.add_renderer` |
| | | - :meth:`pyramid.config.Configurator.add_route_predicate` |
| | | - :meth:`pyramid.config.Configurator.add_subscriber_predicate` |
| | | - :meth:`pyramid.config.Configurator.add_view_predicate` |
| | | - :meth:`pyramid.config.Configurator.set_authorization_policy` |
| | | - :meth:`pyramid.config.Configurator.set_default_permission` |
| | | - :meth:`pyramid.config.Configurator.set_view_mapper` |
| | | |
| | | :const:`pyramid.config.PHASE2_CONFIG` |
| | | |
| | | - :meth:`pyramid.config.Configurator.add_route` |
| | | - :meth:`pyramid.config.Configurator.set_authentication_policy` |
| | | |
| | | :const:`pyramid.config.PHASE3_CONFIG` |
| | | |
| | | - The default for all builtin or custom directives unless otherwise specified. |
| | | |
| | | Calling Actions From Actions |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | Pyramid's configurator allows actions to be added during a commit-cycle as |
| | | long as they are added to the current or a later ``order`` phase. This means |
| | | that your custom action can defer decisions until commit-time and then do |
| | | things like invoke :meth:`pyramid.config.Configurator.add_route`. It can also |
| | | provide better conflict detection if your addon needs to call more than one |
| | | other action. |
| | | |
| | | For example, let's make an addon that invokes ``add_route`` and ``add_view``, |
| | | but we want it to conflict with any other call to our addon: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.config import PHASE0_CONFIG |
| | | |
| | | def includeme(config): |
| | | config.add_directive('add_auto_route', add_auto_route) |
| | | |
| | | def add_auto_route(config, name, view): |
| | | def register(): |
| | | config.add_view(route_name=name, view=view) |
| | | config.add_route(name, '/' + name) |
| | | config.action(('auto route', name), register, order=PHASE0_CONFIG) |
| | | |
| | | Now someone else can use your addon and be informed if there is a conflict |
| | | between this route and another, or two calls to ``add_auto_route``. |
| | | Notice how we had to invoke our action **before** ``add_view`` or |
| | | ``add_route``. If we tried to invoke this afterward, the subsequent calls to |
| | | ``add_view`` and ``add_route`` would cause conflicts because that phase had |
| | | already been executed, and the configurator cannot go back in time to add more |
| | | views during that commit-cycle. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.config import Configurator |
| | | |
| | | def main(global_config, **settings): |
| | | config = Configurator() |
| | | config.include('auto_route_addon') |
| | | config.add_auto_route('foo', my_view) |
| | | |
| | | def my_view(request): |
| | | return request.response |
| | | |
| | | .. _introspection: |
| | | |
| | | Adding Configuration Introspection |
| | |
| | | A principal is usually a user id, however it also may be a group id if your |
| | | authentication system provides group information and the effective |
| | | :term:`authentication policy` policy is written to respect group information. |
| | | For example, the |
| | | :class:`pyramid.authentication.RepozeWho1AuthenticationPolicy` respects group |
| | | information if you configure it with a ``callback``. |
| | | See :ref:`extending_default_authentication_policies`. |
| | | |
| | | Each ACE in an ACL is processed by an authorization policy *in the |
| | | order dictated by the ACL*. So if you have an ACL like this: |
| | |
| | | :meth:`~pyramid.request.Request.has_permission` fails is often useful. |
| | | |
| | | .. index:: |
| | | single: authentication policy (extending) |
| | | |
| | | .. _extending_default_authentication_policies: |
| | | |
| | | Extending Default Authentication Policies |
| | | ----------------------------------------- |
| | | |
| | | Pyramid ships with some builtin authentication policies for use in your |
| | | applications. See :mod:`pyramid.authentication` for the available |
| | | policies. They differ on their mechanisms for tracking authentication |
| | | credentials between requests, however they all interface with your |
| | | application in mostly the same way. |
| | | |
| | | Above you learned about :ref:`assigning_acls`. Each :term:`principal` used |
| | | in the :term:`ACL` is matched against the list returned from |
| | | :meth:`pyramid.interfaces.IAuthenticationPolicy.effective_principals`. |
| | | Similarly, :meth:`pyramid.request.Request.authenticated_userid` maps to |
| | | :meth:`pyramid.interfaces.IAuthenticationPolicy.authenticated_userid`. |
| | | |
| | | You may control these values by subclassing the default authentication |
| | | policies. For example, below we subclass the |
| | | :class:`pyramid.authentication.AuthTktAuthenticationPolicy` and define |
| | | extra functionality to query our database before confirming that the |
| | | :term:`userid` is valid in order to avoid blindly trusting the value in the |
| | | cookie (what if the cookie is still valid but the user has deleted their |
| | | account?). We then use that :term:`userid` to augment the |
| | | ``effective_principals`` with information about groups and other state for |
| | | that user. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | |
| | | class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): |
| | | def authenticated_userid(self, request): |
| | | userid = self.unauthenticated_userid(request) |
| | | if userid: |
| | | if request.verify_userid_is_still_valid(userid): |
| | | return userid |
| | | |
| | | def effective_principals(self, request): |
| | | principals = [Everyone] |
| | | userid = self.authenticated_userid(request) |
| | | if userid: |
| | | principals += [Authenticated, str(userid)] |
| | | return principals |
| | | |
| | | In most instances ``authenticated_userid`` and ``effective_principals`` are |
| | | application-specific whereas ``unauthenticated_userid``, ``remember`` and |
| | | ``forget`` are generic and focused on transport/serialization of data |
| | | between consecutive requests. |
| | | |
| | | .. index:: |
| | | single: authentication policy (creating) |
| | | |
| | | .. _creating_an_authentication_policy: |
| | |
| | | """ |
| | | |
| | | After you do so, you can pass an instance of such a class into the |
| | | :class:`~pyramid.config.Configurator.set_authentication_policy` method |
| | | :class:`~pyramid.config.Configurator.set_authentication_policy` method at |
| | | configuration time to use it. |
| | | |
| | | .. index:: |
| | |
| | | :linenos: |
| | | |
| | | config.add_route('idea', 'site/{id}') |
| | | config.add_view('mypackage.views.site_view', route_name='idea') |
| | | config.scan() |
| | | |
| | | When a route configuration with a ``view`` attribute is added to the system, |
| | | and an incoming request matches the *pattern* of the route configuration, the |
| | | :term:`view callable` named as the ``view`` attribute of the route |
| | | configuration will be invoked. |
| | | |
| | | In the case of the above example, when the URL of a request matches |
| | | ``/site/{id}``, the view callable at the Python dotted path name |
| | | ``mypackage.views.site_view`` will be called with the request. In other |
| | | words, we've associated a view callable directly with a route pattern. |
| | | Recall that the ``@view_config`` is equivalent to calling ``config.add_view``, |
| | | because the ``config.scan()`` call will import ``mypackage.views``, shown |
| | | below, and execute ``config.add_view`` under the hood. Each view then maps the |
| | | route name to the matching view callable. In the case of the above |
| | | example, when the URL of a request matches ``/site/{id}``, the view callable at |
| | | the Python dotted path name ``mypackage.views.site_view`` will be called with |
| | | the request. In other words, we've associated a view callable directly with a |
| | | route pattern. |
| | | |
| | | When the ``/site/{id}`` route pattern matches during a request, the |
| | | ``site_view`` view callable is invoked with that request as its sole |
| | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.view import view_config |
| | | from pyramid.response import Response |
| | | |
| | | @view_config(route_name='idea') |
| | | def site_view(request): |
| | | return Response(request.matchdict['id']) |
| | | |
| | |
| | | config.add_route('idea', 'ideas/{idea}') |
| | | config.add_route('user', 'users/{user}') |
| | | config.add_route('tag', 'tags/{tag}') |
| | | config.scan() |
| | | |
| | | config.add_view('mypackage.views.idea_view', route_name='idea') |
| | | config.add_view('mypackage.views.user_view', route_name='user') |
| | | config.add_view('mypackage.views.tag_view', route_name='tag') |
| | | Here is an example of a corresponding ``mypackage.views`` module: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.view import view_config |
| | | from pyramid.response import Response |
| | | |
| | | @view_config(route_name='idea') |
| | | def idea_view(request): |
| | | return Response(request.matchdict['id']) |
| | | |
| | | @view_config(route_name='user') |
| | | def user_view(request): |
| | | user = request.matchdict['user'] |
| | | return Response(u'The user is {}.'.format(user)) |
| | | |
| | | @view_config(route_name='tag') |
| | | def tag_view(request): |
| | | tag = request.matchdict['tag'] |
| | | return Response(u'The tag is {}.'.format(tag)) |
| | | |
| | | The above configuration will allow :app:`Pyramid` to service URLs in these |
| | | forms: |
| | | |
| | |
| | | :linenos: |
| | | |
| | | config.add_route('idea', 'ideas/{idea}', factory='myproject.resources.Idea') |
| | | config.add_view('myproject.views.idea_view', route_name='idea') |
| | | config.scan() |
| | | |
| | | The above route will manufacture an ``Idea`` resource as a :term:`context`, |
| | | assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a |
| | |
| | | pass |
| | | |
| | | In a more complicated application, this root factory might be a class |
| | | representing a :term:`SQLAlchemy` model. |
| | | representing a :term:`SQLAlchemy` model. The view ``mypackage.views.idea_view`` |
| | | might look like this: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | @view_config(route_name='idea') |
| | | def idea_view(request): |
| | | idea = request.context |
| | | return Response(idea) |
| | | |
| | | Here, ``request.context`` is an instance of ``Idea``. If indeed the resource |
| | | object is a SQLAlchemy model, you do not even have to perform a query in the |
| | | view callable, since you have access to the resource via ``request.context``. |
| | | |
| | | See :ref:`route_factories` for more details about how to use route factories. |
| | | |
| | |
| | | # True if we are running on Python 3. |
| | | PY3 = sys.version_info[0] == 3 |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | string_types = str, |
| | | integer_types = int, |
| | | class_types = type, |
| | |
| | | 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 |
| | | |
| | | return s |
| | | |
| | | 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 |
| | | if isinstance(s, text_type): |
| | | return s.encode(encoding, errors) |
| | | return s |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | def ascii_native_(s): |
| | | if isinstance(s, text_type): |
| | | s = s.encode('ascii') |
| | |
| | | """ |
| | | |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | def native_(s, encoding='latin-1', errors='strict'): |
| | | """ If ``s`` is an instance of ``text_type``, return |
| | | ``s``, otherwise return ``str(s, encoding, errors)``""" |
| | |
| | | ``s.encode(encoding, errors)``, otherwise return ``str(s)`` |
| | | """ |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | from urllib import parse |
| | | urlparse = parse |
| | | from urllib.parse import quote as url_quote |
| | |
| | | return d.iterkeys() |
| | | |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | def map_(*arg): |
| | | return list(map(*arg)) |
| | | else: |
| | | map_ = map |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | def is_nonstr_iter(v): |
| | | if isinstance(v, str): |
| | | return False |
| | |
| | | def is_nonstr_iter(v): |
| | | return hasattr(v, '__iter__') |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | im_func = '__func__' |
| | | im_self = '__self__' |
| | | else: |
| | | im_func = 'im_func' |
| | | im_self = 'im_self' |
| | | |
| | | try: # pragma: no cover |
| | | try: |
| | | import configparser |
| | | except ImportError: # pragma: no cover |
| | | except ImportError: |
| | | import ConfigParser as configparser |
| | | |
| | | try: |
| | | from Cookie import SimpleCookie |
| | | except ImportError: # pragma: no cover |
| | | from http.cookies import SimpleCookie |
| | | except ImportError: |
| | | from Cookie import SimpleCookie |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | from html import escape |
| | | else: |
| | | from cgi import escape |
| | | |
| | | try: # pragma: no cover |
| | | input_ = raw_input |
| | | except NameError: # pragma: no cover |
| | | if PY3: |
| | | input_ = input |
| | | else: |
| | | input_ = raw_input |
| | | |
| | | |
| | | # support annotations and keyword-only arguments in PY3 |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | from inspect import getfullargspec as getargspec |
| | | else: |
| | | from inspect import getargspec |
| | | |
| | | try: |
| | | from StringIO import StringIO as NativeIO |
| | | except ImportError: # pragma: no cover |
| | | if PY3: |
| | | from io import StringIO as NativeIO |
| | | else: |
| | | from io import BytesIO 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 |
| | | if PY3: |
| | | # see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before |
| | | # decoding it to utf-8 |
| | | def decode_path_info(path): |
| | |
| | | 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 |
| | | if PY3: |
| | | # 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): |
| | |
| | | def is_bound_method(ob): |
| | | return inspect.ismethod(ob) and getattr(ob, im_self, None) is not None |
| | | |
| | | # support annotations and keyword-only arguments in PY3 |
| | | if PY3: # pragma: no cover |
| | | from inspect import getfullargspec as getargspec |
| | | else: |
| | | from inspect import getargspec |
| | | |
| | | if PY3: # pragma: no cover |
| | | from itertools import zip_longest |
| | | else: |
| | | from itertools import izip_longest as zip_longest |
| | | |
| | | def is_unbound_method(fn): |
| | | """ |
| | |
| | | is_bound = is_bound_method(fn) |
| | | |
| | | if not is_bound and inspect.isroutine(fn): |
| | | spec = inspect.getargspec(fn) |
| | | spec = getargspec(fn) |
| | | has_self = len(spec.args) > 0 and spec.args[0] == 'self' |
| | | |
| | | if PY3 and inspect.isfunction(fn) and has_self: # pragma: no cover |
| | |
| | | IDebugLogger, |
| | | IExceptionResponse, |
| | | IPredicateList, |
| | | PHASE0_CONFIG, |
| | | PHASE1_CONFIG, |
| | | PHASE2_CONFIG, |
| | | PHASE3_CONFIG, |
| | | ) |
| | | |
| | | from pyramid.asset import resolve_asset_spec |
| | |
| | | text_, |
| | | reraise, |
| | | string_types, |
| | | zip_longest, |
| | | ) |
| | | |
| | | from pyramid.events import ApplicationCreated |
| | |
| | | from pyramid.threadlocal import manager |
| | | |
| | | from pyramid.util import ( |
| | | ActionInfo, |
| | | WeakOrderedSet, |
| | | action_method, |
| | | object_description, |
| | | ) |
| | | |
| | |
| | | |
| | | from pyramid.path import DottedNameResolver |
| | | |
| | | from pyramid.util import ( |
| | | action_method, |
| | | ActionInfo, |
| | | ) |
| | | |
| | | empty = text_('') |
| | | _marker = object() |
| | | |
| | |
| | | |
| | | not_ = not_ # pyflakes, this is an API |
| | | |
| | | PHASE0_CONFIG = PHASE0_CONFIG # api |
| | | PHASE1_CONFIG = PHASE1_CONFIG # api |
| | | PHASE2_CONFIG = PHASE2_CONFIG # api |
| | | PHASE3_CONFIG = PHASE3_CONFIG # api |
| | | |
| | | class Configurator( |
| | | TestingConfiguratorMixin, |
| | |
| | | >>> output |
| | | [('f', (1,), {}), ('f', (2,), {})] |
| | | |
| | | """ |
| | | The execution is re-entrant such that actions may be added by other |
| | | actions with the one caveat that the order of any added actions must |
| | | be equal to or larger than the current action. |
| | | |
| | | >>> output = [] |
| | | >>> def f(*a, **k): |
| | | ... output.append(('f', a, k)) |
| | | ... context.actions.append((3, g, (8,), {})) |
| | | >>> def g(*a, **k): |
| | | ... output.append(('g', a, k)) |
| | | >>> context.actions = [ |
| | | ... (1, f, (1,)), |
| | | ... ] |
| | | >>> context.execute_actions() |
| | | >>> output |
| | | [('f', (1,), {}), ('g', (8,), {})] |
| | | |
| | | """ |
| | | try: |
| | | for action in resolveConflicts(self.actions): |
| | | all_actions = [] |
| | | executed_actions = [] |
| | | pending_actions = iter([]) |
| | | |
| | | # resolve the new action list against what we have already |
| | | # executed -- if a new action appears intertwined in the list |
| | | # of already-executed actions then someone wrote a broken |
| | | # re-entrant action because it scheduled the action *after* it |
| | | # should have been executed (as defined by the action order) |
| | | def resume(actions): |
| | | for a, b in zip_longest(actions, executed_actions): |
| | | if b is None and a is not None: |
| | | # common case is that we are executing every action |
| | | yield a |
| | | elif b is not None and a != b: |
| | | raise ConfigurationError( |
| | | 'During execution a re-entrant action was added ' |
| | | 'that modified the planned execution order in a ' |
| | | 'way that is incompatible with what has already ' |
| | | 'been executed.') |
| | | else: |
| | | # resolved action is in the same location as before, |
| | | # so we are in good shape, but the action is already |
| | | # executed so we skip it |
| | | assert b is not None and a == b |
| | | |
| | | while True: |
| | | # We clear the actions list prior to execution so if there |
| | | # are some new actions then we add them to the mix and resolve |
| | | # conflicts again. This orders the new actions as well as |
| | | # ensures that the previously executed actions have no new |
| | | # conflicts. |
| | | if self.actions: |
| | | # Only resolve the new actions against executed_actions |
| | | # and pending_actions instead of everything to avoid |
| | | # redundant checks. |
| | | # Assume ``actions = resolveConflicts([A, B, C])`` which |
| | | # after conflict checks, resulted in ``actions == [A]`` |
| | | # then we know action A won out or a conflict would have |
| | | # been raised. Thus, when action D is added later, we only |
| | | # need to check the new action against A. |
| | | # ``actions = resolveConflicts([A, D]) should drop the |
| | | # number of redundant checks down from O(n^2) closer to |
| | | # O(n lg n). |
| | | all_actions.extend(self.actions) |
| | | pending_actions = resume(resolveConflicts( |
| | | executed_actions + |
| | | list(pending_actions) + |
| | | self.actions |
| | | )) |
| | | self.actions = [] |
| | | |
| | | action = next(pending_actions, None) |
| | | if action is None: |
| | | # we are done! |
| | | break |
| | | |
| | | callable = action['callable'] |
| | | args = action['args'] |
| | | kw = action['kw'] |
| | |
| | | if introspector is not None: |
| | | for introspectable in introspectables: |
| | | introspectable.register(introspector, info) |
| | | |
| | | |
| | | executed_actions.append(action) |
| | | |
| | | finally: |
| | | if clear: |
| | | del self.actions[:] |
| | | else: |
| | | self.actions = all_actions |
| | | |
| | | # this function is licensed under the ZPL (stolen from Zope) |
| | | def resolveConflicts(actions): |
| | |
| | | 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): |
| | | if (includepath[:len(basepath)] != basepath or # not a prefix |
| | | 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 |
| | | # 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: |
| | |
| | | ) |
| | | |
| | | global_registries = WeakOrderedSet() |
| | | |
| | |
| | | """ |
| | | def __init__(self, package, prefix): |
| | | self.package = package |
| | | if hasattr(package, '__name__'): |
| | | self.pkg_name = package.__name__ |
| | | else: |
| | | self.pkg_name = package |
| | | self.prefix = prefix |
| | | |
| | | def get_path(self, resource_name): |
| | |
| | | |
| | | def get_filename(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if pkg_resources.resource_exists(self.package, path): |
| | | return pkg_resources.resource_filename(self.package, path) |
| | | if pkg_resources.resource_exists(self.pkg_name, path): |
| | | return pkg_resources.resource_filename(self.pkg_name, path) |
| | | |
| | | def get_stream(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if pkg_resources.resource_exists(self.package, path): |
| | | return pkg_resources.resource_stream(self.package, path) |
| | | if pkg_resources.resource_exists(self.pkg_name, path): |
| | | return pkg_resources.resource_stream(self.pkg_name, path) |
| | | |
| | | def get_string(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if pkg_resources.resource_exists(self.package, path): |
| | | return pkg_resources.resource_string(self.package, path) |
| | | if pkg_resources.resource_exists(self.pkg_name, path): |
| | | return pkg_resources.resource_string(self.pkg_name, path) |
| | | |
| | | def exists(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if pkg_resources.resource_exists(self.package, path): |
| | | if pkg_resources.resource_exists(self.pkg_name, path): |
| | | return True |
| | | |
| | | def isdir(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if pkg_resources.resource_exists(self.package, path): |
| | | return pkg_resources.resource_isdir(self.package, path) |
| | | if pkg_resources.resource_exists(self.pkg_name, path): |
| | | return pkg_resources.resource_isdir(self.pkg_name, path) |
| | | |
| | | def listdir(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if pkg_resources.resource_exists(self.package, path): |
| | | return pkg_resources.resource_listdir(self.package, path) |
| | | if pkg_resources.resource_exists(self.pkg_name, path): |
| | | return pkg_resources.resource_listdir(self.pkg_name, path) |
| | | |
| | | |
| | | class FSAssetSource(object): |
| | |
| | | |
| | | from pyramid.util import ( |
| | | action_method, |
| | | InstancePropertyMixin, |
| | | get_callable_name, |
| | | InstancePropertyHelper, |
| | | ) |
| | | |
| | | |
| | |
| | | |
| | | property = property or reify |
| | | if property: |
| | | name, callable = InstancePropertyMixin._make_property( |
| | | name, callable = InstancePropertyHelper.make_property( |
| | | callable, name=name, reify=reify) |
| | | elif name is None: |
| | | name = callable.__name__ |
| | |
| | | |
| | | 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 |
| | | if result.__class__ is Response: # potential common case |
| | | response = result |
| | | else: |
| | | registry = self.registry |
| | |
| | | name=renderer_name, |
| | | package=self.kw.get('package'), |
| | | registry=registry) |
| | | else: |
| | | renderer = view_renderer.clone() |
| | | |
| | | if '__view__' in attrs: |
| | | view_inst = attrs.pop('__view__') |
| | | else: |
| | |
| | | |
| | | 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 |
| | | if result.__class__ is Response: # common case |
| | | response = result |
| | | else: |
| | | response = registry.queryAdapterOrSelf(result, IResponse) |
| | |
| | | if decorator is None: |
| | | return view |
| | | return decorator(view) |
| | | |
| | | |
| | | @implementer(IViewMapper) |
| | | @provider(IViewMapperFactory) |
| | |
| | | cb = self._default_cachebust() |
| | | if cb: |
| | | def cachebust(subpath, kw): |
| | | token = cb.token(spec + subpath) |
| | | subpath_tuple = tuple(subpath.split('/')) |
| | | subpath_tuple, kw = cb.pregenerate(token, subpath_tuple, kw) |
| | | subpath_tuple, kw = cb.pregenerate( |
| | | spec + subpath, subpath_tuple, kw) |
| | | return '/'.join(subpath_tuple), kw |
| | | else: |
| | | cachebust = None |
| | |
| | | status_map = {} |
| | | code = None |
| | | for name, value in list(globals().items()): |
| | | if (isinstance(value, class_types) and |
| | | issubclass(value, HTTPException) |
| | | and not name.startswith('_')): |
| | | if ( |
| | | isinstance(value, class_types) and |
| | | issubclass(value, HTTPException) and |
| | | not name.startswith('_') |
| | | ): |
| | | code = getattr(value, 'code', None) |
| | | if code: |
| | | status_map[code] = value |
| | |
| | | """Like ``ugettext()``, but look the message up in the specified |
| | | domain. |
| | | """ |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | return self._domains.get(domain, self).gettext(message) |
| | | else: # pragma: no cover |
| | | else: |
| | | return self._domains.get(domain, self).ugettext(message) |
| | | |
| | | def dngettext(self, domain, singular, plural, num): |
| | |
| | | """Like ``ungettext()`` but look the message up in the specified |
| | | domain. |
| | | """ |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | return self._domains.get(domain, self).ngettext( |
| | | singular, plural, num) |
| | | else: # pragma: no cover |
| | | else: |
| | | return self._domains.get(domain, self).ungettext( |
| | | singular, plural, num) |
| | | |
| | |
| | | settings = Attribute('The deployment settings dictionary related ' |
| | | 'to the current application') |
| | | |
| | | def clone(): |
| | | """ Return a shallow copy that does not share any mutable state.""" |
| | | |
| | | class IRendererFactory(Interface): |
| | | def __call__(info): |
| | | """ Return an object that implements |
| | |
| | | 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``""" |
| | | """ Return an instance of ``pyramid.request.Request``""" |
| | | |
| | | def blank(path): |
| | | """ Return an empty request object (see |
| | |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def token(pathspec): |
| | | """ |
| | | Computes and returns a token string used for cache busting. |
| | | ``pathspec`` is the path specification for the resource to be cache |
| | | busted. """ |
| | | |
| | | def pregenerate(token, subpath, kw): |
| | | def pregenerate(pathspec, subpath, kw): |
| | | """ |
| | | Modifies a subpath and/or keyword arguments from which a static asset |
| | | URL will be computed during URL generation. The ``token`` argument is |
| | | a token string computed by |
| | | :meth:`~pyramid.interfaces.ICacheBuster.token` for a particular asset. |
| | | URL will be computed during URL generation. The ``pathspec`` argument |
| | | is the path specification for the resource to be cache busted. |
| | | The ``subpath`` argument is a tuple of path elements that represent the |
| | | portion of the asset URL which is used to find the asset. The ``kw`` |
| | | argument is a dict of keywords that are to be passed eventually to |
| | |
| | | # with this phase will be executed earlier than those with later phase |
| | | # numbers. The default phase number is 0, FTR. |
| | | |
| | | PHASE0_CONFIG = -30 |
| | | PHASE1_CONFIG = -20 |
| | | PHASE2_CONFIG = -10 |
| | | |
| | | PHASE3_CONFIG = 0 |
| | |
| | | import contextlib |
| | | import json |
| | | import os |
| | | |
| | |
| | | helper = RendererHelper(name=renderer_name, package=package, |
| | | registry=registry) |
| | | |
| | | saved_response = None |
| | | # save the current response, preventing the renderer from affecting it |
| | | attrs = request.__dict__ if request is not None else {} |
| | | if 'response' in attrs: |
| | | saved_response = attrs['response'] |
| | | del attrs['response'] |
| | | |
| | | result = helper.render(value, None, request=request) |
| | | |
| | | # restore the original response, overwriting any changes |
| | | if saved_response is not None: |
| | | attrs['response'] = saved_response |
| | | elif 'response' in attrs: |
| | | del attrs['response'] |
| | | with temporary_response(request): |
| | | result = helper.render(value, None, request=request) |
| | | |
| | | return result |
| | | |
| | | def render_to_response(renderer_name, value, request=None, package=None): |
| | | def render_to_response(renderer_name, |
| | | value, |
| | | request=None, |
| | | package=None, |
| | | response=None): |
| | | """ Using the renderer ``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 |
| | |
| | | |
| | | 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. |
| | | in particular). Keep in mind that any changes made to ``request.response`` |
| | | prior to calling this function will not be reflected in the resulting |
| | | response object. A new response object will be created for each call |
| | | unless one is passed as the ``response`` argument. |
| | | |
| | | .. versionchanged:: 1.6 |
| | | In previous versions, any changes made to ``request.response`` outside |
| | | of this function call would affect the returned response. This is no |
| | | longer the case. If you wish to send in a pre-initialized response |
| | | then you may pass one in the ``response`` argument. |
| | | |
| | | """ |
| | | try: |
| | |
| | | package = caller_package() |
| | | helper = RendererHelper(name=renderer_name, package=package, |
| | | registry=registry) |
| | | return helper.render_to_response(value, None, request=request) |
| | | |
| | | with temporary_response(request): |
| | | if response is not None: |
| | | request.response = response |
| | | result = helper.render_to_response(value, None, request=request) |
| | | |
| | | return result |
| | | |
| | | @contextlib.contextmanager |
| | | def temporary_response(request): |
| | | """ |
| | | Temporarily delete request.response and restore it afterward. |
| | | """ |
| | | saved_response = None |
| | | # save the current response, preventing the renderer from affecting it |
| | | attrs = request.__dict__ if request is not None else {} |
| | | if 'response' in attrs: |
| | | saved_response = attrs['response'] |
| | | del attrs['response'] |
| | | |
| | | yield |
| | | |
| | | # restore the original response, overwriting any changes |
| | | if saved_response is not None: |
| | | attrs['response'] = saved_response |
| | | elif 'response' in attrs: |
| | | del attrs['response'] |
| | | |
| | | def get_renderer(renderer_name, package=None): |
| | | """ Return the renderer object for the renderer ``renderer_name``. |
| | |
| | | |
| | | from pyramid.interfaces import ( |
| | | IRequest, |
| | | IRequestExtensions, |
| | | IResponse, |
| | | ISessionFactory, |
| | | ) |
| | |
| | | text_, |
| | | bytes_, |
| | | native_, |
| | | iteritems_, |
| | | ) |
| | | |
| | | from pyramid.decorator import reify |
| | |
| | | AuthorizationAPIMixin, |
| | | ) |
| | | from pyramid.url import URLMethodsMixin |
| | | from pyramid.util import InstancePropertyMixin |
| | | from pyramid.util import ( |
| | | InstancePropertyHelper, |
| | | InstancePropertyMixin, |
| | | ) |
| | | |
| | | class TemplateContext(object): |
| | | pass |
| | |
| | | new_request.environ['PATH_INFO'] = new_path_info |
| | | |
| | | return new_request.get_response(app) |
| | | |
| | | def apply_request_extensions(request, extensions=None): |
| | | """Apply request extensions (methods and properties) to an instance of |
| | | :class:`pyramid.interfaces.IRequest`. This method is dependent on the |
| | | ``request`` containing a properly initialized registry. |
| | | |
| | | After invoking this method, the ``request`` should have the methods |
| | | and properties that were defined using |
| | | :meth:`pyramid.config.Configurator.add_request_method`. |
| | | """ |
| | | if extensions is None: |
| | | extensions = request.registry.queryUtility(IRequestExtensions) |
| | | if extensions is not None: |
| | | for name, fn in iteritems_(extensions.methods): |
| | | method = fn.__get__(request, request.__class__) |
| | | setattr(request, name, method) |
| | | |
| | | InstancePropertyHelper.apply_properties( |
| | | request, extensions.descriptors) |
| | |
| | | from pyramid.exceptions import PredicateMismatch |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | from pyramid.request import Request |
| | | from pyramid.request import apply_request_extensions |
| | | from pyramid.threadlocal import manager |
| | | |
| | | from pyramid.traversal import ( |
| | |
| | | try: |
| | | extensions = self.request_extensions |
| | | if extensions is not None: |
| | | request._set_extensions(extensions) |
| | | apply_request_extensions(request, extensions=extensions) |
| | | response = handle_request(request) |
| | | |
| | | if request.response_callbacks: |
| | |
| | | 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('---')]) |
| | | 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: |
| | |
| | | import time |
| | | |
| | | try: |
| | | import http.client as httplib |
| | | except ImportError: |
| | | import httplib |
| | | except ImportError: # pragma: no cover |
| | | import http.client as httplib # py3 |
| | | |
| | | |
| | | class TemplateTest(object): |
| | | def make_venv(self, directory): # pragma: no cover |
| | | def make_venv(self, directory): # pragma: no cover |
| | | import virtualenv |
| | | from virtualenv import Logger |
| | | logger = Logger([(Logger.level_for_integer(2), sys.stdout)]) |
| | |
| | | clear=False, |
| | | unzip_setuptools=True) |
| | | |
| | | def install(self, tmpl_name): # pragma: no cover |
| | | def install(self, tmpl_name): # pragma: no cover |
| | | try: |
| | | self.old_cwd = os.getcwd() |
| | | self.directory = tempfile.mkdtemp() |
| | |
| | | from pyramid.config import global_registries |
| | | from pyramid.exceptions import ConfigurationError |
| | | from pyramid.request import Request |
| | | |
| | | from pyramid.interfaces import ( |
| | | IRequestExtensions, |
| | | IRequestFactory, |
| | | IRootFactory, |
| | | ) |
| | | from pyramid.request import Request |
| | | from pyramid.request import apply_request_extensions |
| | | |
| | | from pyramid.threadlocal import manager as threadlocal_manager |
| | | from pyramid.traversal import DefaultRootFactory |
| | |
| | | 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) |
| | | apply_request_extensions(request) |
| | | def closer(): |
| | | threadlocal_manager.pop() |
| | | root_factory = registry.queryUtility(IRootFactory, |
| | |
| | | |
| | | MAXFD = 1024 |
| | | |
| | | try: |
| | | import termios |
| | | except ImportError: # pragma: no cover |
| | | termios = None |
| | | |
| | | if WIN and not hasattr(os, 'kill'): # pragma: no cover |
| | | # py 2.6 on windows |
| | | def kill(pid, sig=None): |
| | |
| | | print(msg) |
| | | |
| | | def get_options(self): |
| | | if (len(self.args) > 1 |
| | | and self.args[1] in self.possible_subcommands): |
| | | 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 |
| | | def run(self): # pragma: no cover |
| | | if self.options.stop_daemon: |
| | | return self.stop_daemon() |
| | | |
| | |
| | | return 2 |
| | | app_spec = self.args[0] |
| | | |
| | | if (len(self.args) > 1 |
| | | and self.args[1] in self.possible_subcommands): |
| | | if ( |
| | | len(self.args) > 1 and |
| | | self.args[1] in self.possible_subcommands |
| | | ): |
| | | cmd = self.args[1] |
| | | else: |
| | | cmd = None |
| | |
| | | self.out(str(ex)) |
| | | return 2 |
| | | |
| | | if (self.options.monitor_restart |
| | | and not os.environ.get(self._monitor_environ_key)): |
| | | if ( |
| | | self.options.monitor_restart and |
| | | not os.environ.get(self._monitor_environ_key) |
| | | ): |
| | | return self.restart_with_monitor() |
| | | |
| | | if self.options.pid_file: |
| | |
| | | def open_browser(): |
| | | context = loadcontext(SERVER, app_spec, name=app_name, relative_to=base, |
| | | global_conf=vars) |
| | | url = 'http://{host}:{port}/'.format(**context.config()) |
| | | url = 'http://127.0.0.1:{port}/'.format(**context.config()) |
| | | time.sleep(1) |
| | | webbrowser.open(url) |
| | | t = threading.Thread(target=open_browser) |
| | |
| | | raise SystemExit |
| | | signal.signal(signal.SIGTERM, handle_term) |
| | | |
| | | def ensure_echo_on(): # pragma: no cover |
| | | if termios: |
| | | fd = sys.stdin |
| | | if fd.isatty(): |
| | | attr_list = termios.tcgetattr(fd) |
| | | if not attr_list[3] & termios.ECHO: |
| | | attr_list[3] |= termios.ECHO |
| | | termios.tcsetattr(fd, termios.TCSANOW, attr_list) |
| | | |
| | | 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). |
| | | thread does, causing ports to remain open/locked. |
| | | """ |
| | | ensure_echo_on() |
| | | mon = Monitor(poll_interval=poll_interval) |
| | | if extra_files is None: |
| | | extra_files = [] |
| | |
| | | |
| | | .. versionadded:: 1.4a2 |
| | | """ |
| | | supplied_token = request.params.get(token, request.headers.get(header)) |
| | | if supplied_token != request.session.get_csrf_token(): |
| | | supplied_token = request.params.get(token, request.headers.get(header, "")) |
| | | if strings_differ(request.session.get_csrf_token(), supplied_token): |
| | | if raises: |
| | | raise BadCSRFToken('check_csrf_token(): Invalid token') |
| | | return False |
| | |
| | | def __init__(self): |
| | | self.token_cache = {} |
| | | |
| | | def token(self, pathspec): |
| | | def tokenize(self, pathspec): |
| | | # An astute observer will notice that this use of token_cache doesn't |
| | | # look particularly thread safe. Basic read/write operations on Python |
| | | # dicts, however, are atomic, so simply accessing and writing values |
| | |
| | | self.token_cache[pathspec] = token = _generate_md5(pathspec) |
| | | return token |
| | | |
| | | class PathSegmentMd5CacheBuster(Md5AssetTokenGenerator): |
| | | class PathSegmentCacheBuster(object): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which |
| | | inserts a token for cache busting in the path portion of an asset URL. |
| | | |
| | | To use this class, subclass it and provide a ``tokenize`` method which |
| | | accepts a ``pathspec`` and returns a token. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | token = self.tokenize(pathspec) |
| | | return (token,) + subpath, kw |
| | | |
| | | def match(self, subpath): |
| | | return subpath[1:] |
| | | |
| | | class PathSegmentMd5CacheBuster(PathSegmentCacheBuster, |
| | | Md5AssetTokenGenerator): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which |
| | | inserts an md5 checksum token for cache busting in the path portion of an |
| | |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def pregenerate(self, token, subpath, kw): |
| | | return (token,) + subpath, kw |
| | | def __init__(self): |
| | | super(PathSegmentMd5CacheBuster, self).__init__() |
| | | |
| | | def match(self, subpath): |
| | | return subpath[1:] |
| | | class QueryStringCacheBuster(object): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which adds |
| | | a token for cache busting in the query string of an asset URL. |
| | | |
| | | class QueryStringMd5CacheBuster(Md5AssetTokenGenerator): |
| | | The optional ``param`` argument determines the name of the parameter added |
| | | to the query string and defaults to ``'x'``. |
| | | |
| | | To use this class, subclass it and provide a ``tokenize`` method which |
| | | accepts a ``pathspec`` and returns a token. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, param='x'): |
| | | self.param = param |
| | | |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | token = self.tokenize(pathspec) |
| | | query = kw.setdefault('_query', {}) |
| | | if isinstance(query, dict): |
| | | query[self.param] = token |
| | | else: |
| | | kw['_query'] = tuple(query) + ((self.param, token),) |
| | | return subpath, kw |
| | | |
| | | class QueryStringMd5CacheBuster(QueryStringCacheBuster, |
| | | Md5AssetTokenGenerator): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which adds |
| | | an md5 checksum token for cache busting in the query string of an asset |
| | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, param='x'): |
| | | super(QueryStringMd5CacheBuster, self).__init__() |
| | | self.param = param |
| | | super(QueryStringMd5CacheBuster, self).__init__(param=param) |
| | | |
| | | def pregenerate(self, token, subpath, kw): |
| | | query = kw.setdefault('_query', {}) |
| | | if isinstance(query, dict): |
| | | query[self.param] = token |
| | | else: |
| | | kw['_query'] = tuple(query) + ((self.param, token),) |
| | | return subpath, kw |
| | | |
| | | class QueryStringConstantCacheBuster(QueryStringMd5CacheBuster): |
| | | class QueryStringConstantCacheBuster(QueryStringCacheBuster): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which adds |
| | | an arbitrary token for cache busting in the query string of an asset URL. |
| | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, token, param='x'): |
| | | super(QueryStringConstantCacheBuster, self).__init__(param=param) |
| | | self._token = token |
| | | self.param = param |
| | | |
| | | def token(self, pathspec): |
| | | def tokenize(self, pathspec): |
| | | return self._token |
| | | |
| | |
| | | def test_add_response_adapter_dottednames(self): |
| | | from pyramid.interfaces import IResponse |
| | | config = self._makeOne(autocommit=True) |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | str_name = 'builtins.str' |
| | | else: |
| | | str_name = '__builtin__.str' |
| | |
| | | self.assertEqual(source.package, subpackage) |
| | | self.assertEqual(source.prefix, 'templates/bar.pt') |
| | | |
| | | resource_name = '' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_package_with_package(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | self.assertTrue(isinstance(source, PackageAssetSource)) |
| | | self.assertEqual(source.package, subpackage) |
| | | self.assertEqual(source.prefix, '') |
| | | |
| | | resource_name = 'templates/bar.pt' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_directory_with_directory(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | |
| | | self.assertEqual(source.package, subpackage) |
| | | self.assertEqual(source.prefix, 'templates/') |
| | | |
| | | resource_name = 'bar.pt' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_directory_with_package(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | self.assertEqual(source.package, subpackage) |
| | | self.assertEqual(source.prefix, '') |
| | | |
| | | resource_name = 'templates/bar.pt' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_package_with_directory(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | self.assertTrue(isinstance(source, PackageAssetSource)) |
| | | self.assertEqual(source.package, subpackage) |
| | | self.assertEqual(source.prefix, 'templates/') |
| | | |
| | | resource_name = 'bar.pt' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_directory_with_absfile(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | |
| | | self.assertTrue(isinstance(source, FSAssetSource)) |
| | | self.assertEqual(source.prefix, abspath) |
| | | |
| | | resource_name = '' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_directory_with_absdirectory(self): |
| | | from pyramid.config.assets import FSAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | self.assertTrue(isinstance(source, FSAssetSource)) |
| | | self.assertEqual(source.prefix, abspath) |
| | | |
| | | resource_name = 'bar.pt' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test_override_asset_package_with_absdirectory(self): |
| | | from pyramid.config.assets import FSAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | self.assertTrue(isinstance(source, FSAssetSource)) |
| | | self.assertEqual(source.prefix, abspath) |
| | | |
| | | resource_name = 'bar.pt' |
| | | expected = os.path.join(here, 'pkgs', 'asset', |
| | | 'subpackage', 'templates', 'bar.pt') |
| | | self.assertEqual(override.source.get_filename(resource_name), |
| | | expected) |
| | | |
| | | def test__override_not_yet_registered(self): |
| | | from pyramid.interfaces import IPackageOverrides |
| | | package = DummyPackage('package') |
| | |
| | | self.assertRaises(ConfigurationExecutionError, c.execute_actions) |
| | | self.assertEqual(output, [('f', (1,), {}), ('f', (2,), {})]) |
| | | |
| | | def test_reentrant_action(self): |
| | | output = [] |
| | | c = self._makeOne() |
| | | def f(*a, **k): |
| | | output.append(('f', a, k)) |
| | | c.actions.append((3, g, (8,), {})) |
| | | def g(*a, **k): |
| | | output.append(('g', a, k)) |
| | | c.actions = [ |
| | | (1, f, (1,)), |
| | | ] |
| | | c.execute_actions() |
| | | self.assertEqual(output, [('f', (1,), {}), ('g', (8,), {})]) |
| | | |
| | | def test_reentrant_action_error(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | c = self._makeOne() |
| | | def f(*a, **k): |
| | | c.actions.append((3, g, (8,), {}, (), None, -1)) |
| | | def g(*a, **k): pass |
| | | c.actions = [ |
| | | (1, f, (1,)), |
| | | ] |
| | | self.assertRaises(ConfigurationError, c.execute_actions) |
| | | |
| | | def test_reentrant_action_without_clear(self): |
| | | c = self._makeOne() |
| | | def f(*a, **k): |
| | | c.actions.append((3, g, (8,))) |
| | | def g(*a, **k): pass |
| | | c.actions = [ |
| | | (1, f, (1,)), |
| | | ] |
| | | c.execute_actions(clear=False) |
| | | self.assertEqual(c.actions, [ |
| | | (1, f, (1,)), |
| | | (3, g, (8,)), |
| | | ]) |
| | | |
| | | class Test_reentrant_action_functional(unittest.TestCase): |
| | | def _makeConfigurator(self, *arg, **kw): |
| | | from pyramid.config import Configurator |
| | | config = Configurator(*arg, **kw) |
| | | return config |
| | | |
| | | def test_functional(self): |
| | | def add_auto_route(config, name, view): |
| | | def register(): |
| | | config.add_view(route_name=name, view=view) |
| | | config.add_route(name, '/' + name) |
| | | config.action( |
| | | ('auto route', name), register, order=-30 |
| | | ) |
| | | config = self._makeConfigurator() |
| | | config.add_directive('add_auto_route', add_auto_route) |
| | | def my_view(request): return request.response |
| | | config.add_auto_route('foo', my_view) |
| | | config.commit() |
| | | 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, 'foo') |
| | | self.assertEqual(route.path, '/foo') |
| | | |
| | | |
| | | class Test_resolveConflicts(unittest.TestCase): |
| | | def _callFUT(self, actions): |
| | | from pyramid.config import resolveConflicts |
| | |
| | | self.assertEqual(view_inst, view) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | def view(request): |
| | | return 'OK' |
| | | deriver = self._makeOne(renderer=moo()) |
| | |
| | | self.assertEqual(view_inst, 'view') |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | def view(request): |
| | | return 'OK' |
| | | deriver = self._makeOne(renderer=moo()) |
| | |
| | | self.assertEqual(view_inst.__class__, View) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | class View(object): |
| | | def __init__(self, context, request): |
| | | pass |
| | |
| | | self.assertEqual(view_inst.__class__, View) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | class View(object): |
| | | def __init__(self, request): |
| | | pass |
| | |
| | | self.assertEqual(view_inst.__class__, View) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | class View: |
| | | def __init__(self, context, request): |
| | | pass |
| | |
| | | self.assertEqual(view_inst.__class__, View) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | class View: |
| | | def __init__(self, request): |
| | | pass |
| | |
| | | self.assertEqual(view_inst, view) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | class View: |
| | | def index(self, context, request): |
| | | return {'a':'1'} |
| | |
| | | self.assertEqual(view_inst, view) |
| | | self.assertEqual(ctx, context) |
| | | return response |
| | | def clone(self): |
| | | return self |
| | | class View: |
| | | def index(self, request): |
| | | return {'a':'1'} |
| | |
| | | def test_add_cachebust_default(self): |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | | inst._default_cachebust = DummyCacheBuster |
| | | inst._default_cachebust = lambda: DummyCacheBuster('foo') |
| | | inst.add(config, 'view', 'mypackage:path', cachebust=True) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | subpath, kw = cachebust('some/path', {}) |
| | |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'mypackage:path', |
| | | cachebust=DummyCacheBuster()) |
| | | cachebust=DummyCacheBuster('foo')) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | subpath, kw = cachebust('some/path', {}) |
| | | self.assertEqual(subpath, 'some/path') |
| | |
| | | """ """ |
| | | |
| | | class DummyCacheBuster(object): |
| | | def token(self, pathspec): |
| | | return 'foo' |
| | | def pregenerate(self, token, subpath, kw): |
| | | kw['x'] = token |
| | | def __init__(self, token): |
| | | self.token = token |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | kw['x'] = self.token |
| | | return subpath, kw |
| | | |
| | | def parse_httpdate(s): |
| | |
| | | |
| | | def test_zope_dottedname_style_resolve_builtin(self): |
| | | typ = self._makeOne() |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | result = typ._zope_dottedname_style('builtins.str', None) |
| | | else: |
| | | result = typ._zope_dottedname_style('__builtin__.str', None) |
| | |
| | | def tearDown(self): |
| | | testing.tearDown() |
| | | |
| | | def _callFUT(self, renderer_name, value, request=None, package=None): |
| | | def _callFUT(self, renderer_name, value, request=None, package=None, |
| | | response=None): |
| | | from pyramid.renderers import render_to_response |
| | | return render_to_response(renderer_name, value, request=request, |
| | | package=package) |
| | | package=package, response=response) |
| | | |
| | | def test_it_no_request(self): |
| | | renderer = self.config.testing_add_renderer( |
| | |
| | | self.assertEqual(response.body, b'abc') |
| | | renderer.assert_(a=1) |
| | | renderer.assert_(request=request) |
| | | |
| | | def test_response_preserved(self): |
| | | request = testing.DummyRequest() |
| | | response = object() # should error if mutated |
| | | request.response = response |
| | | # use a json renderer, which will mutate the response |
| | | result = self._callFUT('json', dict(a=1), request=request) |
| | | self.assertEqual(result.body, b'{"a": 1}') |
| | | self.assertNotEqual(request.response, result) |
| | | self.assertEqual(request.response, response) |
| | | |
| | | def test_no_response_to_preserve(self): |
| | | from pyramid.decorator import reify |
| | | class DummyRequestWithClassResponse(object): |
| | | _response = DummyResponse() |
| | | _response.content_type = None |
| | | _response.default_content_type = None |
| | | @reify |
| | | def response(self): |
| | | return self._response |
| | | request = DummyRequestWithClassResponse() |
| | | # use a json renderer, which will mutate the response |
| | | result = self._callFUT('json', dict(a=1), request=request) |
| | | self.assertEqual(result.body, b'{"a": 1}') |
| | | self.assertFalse('response' in request.__dict__) |
| | | |
| | | def test_custom_response_object(self): |
| | | class DummyRequestWithClassResponse(object): |
| | | pass |
| | | request = DummyRequestWithClassResponse() |
| | | response = DummyResponse() |
| | | # use a json renderer, which will mutate the response |
| | | result = self._callFUT('json', dict(a=1), request=request, |
| | | response=response) |
| | | self.assertTrue(result is response) |
| | | self.assertEqual(result.body, b'{"a": 1}') |
| | | self.assertFalse('response' in request.__dict__) |
| | | |
| | | class Test_get_renderer(unittest.TestCase): |
| | | def setUp(self): |
| | |
| | | |
| | | class DummyResponse: |
| | | status = '200 OK' |
| | | default_content_type = 'text/html' |
| | | content_type = default_content_type |
| | | headerlist = () |
| | | app_iter = () |
| | | body = '' |
| | | body = b'' |
| | | |
| | | # compat for renderer that will set unicode on py3 |
| | | def _set_text(self, val): # pragma: no cover |
| | | self.body = val.encode('utf8') |
| | | text = property(fset=_set_text) |
| | | |
| | |
| | | b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', |
| | | 'utf-8' |
| | | ) |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | body = bytes(json.dumps({'a':inp}), 'utf-16') |
| | | else: |
| | | body = json.dumps({'a':inp}).decode('utf-8').encode('utf-16') |
| | |
| | | self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded) |
| | | self.assertEqual(request.environ['PATH_INFO'], '/' + encoded) |
| | | |
| | | class DummyRequest: |
| | | class Test_apply_request_extensions(unittest.TestCase): |
| | | def setUp(self): |
| | | self.config = testing.setUp() |
| | | |
| | | def tearDown(self): |
| | | testing.tearDown() |
| | | |
| | | def _callFUT(self, request, extensions=None): |
| | | from pyramid.request import apply_request_extensions |
| | | return apply_request_extensions(request, extensions=extensions) |
| | | |
| | | def test_it_with_registry(self): |
| | | from pyramid.interfaces import IRequestExtensions |
| | | extensions = Dummy() |
| | | extensions.methods = {'foo': lambda x, y: y} |
| | | extensions.descriptors = {'bar': property(lambda x: 'bar')} |
| | | self.config.registry.registerUtility(extensions, IRequestExtensions) |
| | | request = DummyRequest() |
| | | request.registry = self.config.registry |
| | | self._callFUT(request) |
| | | self.assertEqual(request.bar, 'bar') |
| | | self.assertEqual(request.foo('abc'), 'abc') |
| | | |
| | | def test_it_override_extensions(self): |
| | | from pyramid.interfaces import IRequestExtensions |
| | | ignore = Dummy() |
| | | ignore.methods = {'x': lambda x, y, z: 'asdf'} |
| | | ignore.descriptors = {'bar': property(lambda x: 'asdf')} |
| | | self.config.registry.registerUtility(ignore, IRequestExtensions) |
| | | request = DummyRequest() |
| | | request.registry = self.config.registry |
| | | |
| | | extensions = Dummy() |
| | | extensions.methods = {'foo': lambda x, y: y} |
| | | extensions.descriptors = {'bar': property(lambda x: 'bar')} |
| | | self._callFUT(request, extensions=extensions) |
| | | self.assertRaises(AttributeError, lambda: request.x) |
| | | self.assertEqual(request.bar, 'bar') |
| | | self.assertEqual(request.foo('abc'), 'abc') |
| | | |
| | | class Dummy(object): |
| | | pass |
| | | |
| | | class DummyRequest(object): |
| | | def __init__(self, environ=None): |
| | | if environ is None: |
| | | environ = {} |
| | |
| | | from pyramid.interfaces import IRequestExtensions |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.request import Request |
| | | from pyramid.util import InstancePropertyHelper |
| | | context = DummyContext() |
| | | self._registerTraverserFactory(context) |
| | | class Extensions(object): |
| | |
| | | self.methods = {} |
| | | self.descriptors = {} |
| | | extensions = Extensions() |
| | | L = [] |
| | | ext_method = lambda r: 'bar' |
| | | name, fn = InstancePropertyHelper.make_property(ext_method, name='foo') |
| | | extensions.descriptors[name] = fn |
| | | 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) |
| | |
| | | router.request_factory = request_factory |
| | | start_response = DummyStartResponse() |
| | | router(environ, start_response) |
| | | self.assertEqual(L, [extensions]) |
| | | self.assertEqual(view.request.foo, 'bar') |
| | | |
| | | def test_call_view_registered_nonspecific_default_path(self): |
| | | from pyramid.interfaces import IViewClassifier |
| | |
| | | self.assertEqual(request.context, context) |
| | | |
| | | def test_it_with_extensions(self): |
| | | exts = Dummy() |
| | | from pyramid.util import InstancePropertyHelper |
| | | exts = DummyExtensions() |
| | | ext_method = lambda r: 'bar' |
| | | name, fn = InstancePropertyHelper.make_property(ext_method, 'foo') |
| | | exts.descriptors[name] = fn |
| | | request = DummyRequest({}) |
| | | registry = request.registry = self._makeRegistry([exts, DummyFactory]) |
| | | info = self._callFUT(request=request, registry=registry) |
| | | self.assertEqual(request.extensions, exts) |
| | | self.assertEqual(request.foo, 'bar') |
| | | root, closer = info['root'], info['closer'] |
| | | closer() |
| | | |
| | |
| | | def pop(self): |
| | | self.popped.append(True) |
| | | |
| | | class DummyRequest: |
| | | class DummyRequest(object): |
| | | matchdict = None |
| | | matched_route = None |
| | | def __init__(self, environ): |
| | | self.environ = environ |
| | | |
| | | def _set_extensions(self, exts): |
| | | self.extensions = exts |
| | | class DummyExtensions: |
| | | def __init__(self): |
| | | self.descriptors = {} |
| | | self.methods = {} |
New file |
| | |
| | | # this file has a .txt extension to avoid coverage reports |
| | | # since it is not imported but rather the contents are read and exec'd |
| | | foo = 1 |
| | |
| | | import unittest |
| | | |
| | | from pyramid.compat import PY3 |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | import builtins as __builtin__ |
| | | else: |
| | | import __builtin__ |
| | |
| | | os.path.abspath( |
| | | os.path.join( |
| | | os.path.dirname(__file__), |
| | | 'pystartup.py'))) |
| | | 'pystartup.txt'))) |
| | | shell = dummy.DummyShell() |
| | | command.run(shell) |
| | | self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') |
| | |
| | | return cls() |
| | | |
| | | def test_package_resource(self): |
| | | fut = self._makeOne().token |
| | | fut = self._makeOne().tokenize |
| | | expected = '76d653a3a044e2f4b38bb001d283e3d9' |
| | | token = fut('pyramid.tests:fixtures/static/index.html') |
| | | self.assertEqual(token, expected) |
| | | |
| | | def test_filesystem_resource(self): |
| | | fut = self._makeOne().token |
| | | fut = self._makeOne().tokenize |
| | | expected = 'd5155f250bef0e9923e894dbc713c5dd' |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Are we rich yet?") |
| | |
| | | self.assertEqual(token, expected) |
| | | |
| | | def test_cache(self): |
| | | fut = self._makeOne().token |
| | | fut = self._makeOne().tokenize |
| | | expected = 'd5155f250bef0e9923e894dbc713c5dd' |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Are we rich yet?") |
| | |
| | | def _makeOne(self): |
| | | from pyramid.static import PathSegmentMd5CacheBuster as cls |
| | | inst = cls() |
| | | inst.token = lambda pathspec: 'foo' |
| | | inst.tokenize = lambda pathspec: 'foo' |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().token |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | |
| | | inst = cls(param) |
| | | else: |
| | | inst = cls() |
| | | inst.token = lambda pathspec: 'foo' |
| | | inst.tokenize = lambda pathspec: 'foo' |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().token |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().token |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | |
| | | foo = DummyContext(bar, path) |
| | | root = DummyContext(foo, 'root') |
| | | policy = self._makeOne(root) |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | vhm_root = b'/Qu\xc3\xa9bec'.decode('latin-1') |
| | | else: |
| | | vhm_root = b'/Qu\xc3\xa9bec' |
| | |
| | | def test___call__pathinfo_cant_be_decoded(self): |
| | | from pyramid.exceptions import URLDecodeError |
| | | mapper = self._makeOne() |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | path_info = b'\xff\xfe\xe6\x00'.decode('latin-1') |
| | | else: |
| | | path_info = b'\xff\xfe\xe6\x00' |
| | |
| | | from pyramid.compat import PY3 |
| | | |
| | | |
| | | class Test_InstancePropertyHelper(unittest.TestCase): |
| | | def _makeOne(self): |
| | | cls = self._getTargetClass() |
| | | return cls() |
| | | |
| | | def _getTargetClass(self): |
| | | from pyramid.util import InstancePropertyHelper |
| | | return InstancePropertyHelper |
| | | |
| | | def test_callable(self): |
| | | def worker(obj): |
| | | return obj.bar |
| | | foo = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, worker, name='x') |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | self.assertRaises(ValueError, helper.set_property, foo, property(worker)) |
| | | |
| | | def test_property_with_name(self): |
| | | def worker(obj): |
| | | return obj.bar |
| | | foo = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | self.assertRaises(ValueError, helper.set_property, |
| | | foo, property(worker), name='x', reify=True) |
| | | |
| | | def test_override_property(self): |
| | | def worker(obj): pass |
| | | foo = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, worker, name='x') |
| | | def doit(): |
| | | foo.x = 1 |
| | | self.assertRaises(AttributeError, doit) |
| | | |
| | | def test_override_reify(self): |
| | | def worker(obj): pass |
| | | foo = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, lambda _: 1, name='x') |
| | | self.assertEqual(1, foo.x) |
| | | helper.set_property(foo, 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 = Dummy() |
| | | helper = self._getTargetClass() |
| | | helper.set_property(foo, lambda _: 1, name='x', reify=True) |
| | | self.assertEqual(1, foo.x) |
| | | helper.set_property(foo, lambda _: 2, name='x', reify=True) |
| | | self.assertEqual(1, foo.x) |
| | | |
| | | def test_make_property(self): |
| | | from pyramid.decorator import reify |
| | | helper = self._getTargetClass() |
| | | name, fn = helper.make_property(lambda x: 1, name='x', reify=True) |
| | | self.assertEqual(name, 'x') |
| | | self.assertTrue(isinstance(fn, reify)) |
| | | |
| | | def test_apply_properties_with_iterable(self): |
| | | foo = Dummy() |
| | | helper = self._getTargetClass() |
| | | x = helper.make_property(lambda _: 1, name='x', reify=True) |
| | | y = helper.make_property(lambda _: 2, name='y') |
| | | helper.apply_properties(foo, [x, y]) |
| | | self.assertEqual(1, foo.x) |
| | | self.assertEqual(2, foo.y) |
| | | |
| | | def test_apply_properties_with_dict(self): |
| | | foo = Dummy() |
| | | helper = self._getTargetClass() |
| | | x_name, x_fn = helper.make_property(lambda _: 1, name='x', reify=True) |
| | | y_name, y_fn = helper.make_property(lambda _: 2, name='y') |
| | | helper.apply_properties(foo, {x_name: x_fn, y_name: y_fn}) |
| | | self.assertEqual(1, foo.x) |
| | | self.assertEqual(2, foo.y) |
| | | |
| | | def test_make_property_unicode(self): |
| | | from pyramid.compat import text_ |
| | | from pyramid.exceptions import ConfigurationError |
| | | |
| | | cls = self._getTargetClass() |
| | | if PY3: # pragma: nocover |
| | | name = b'La Pe\xc3\xb1a' |
| | | else: # pragma: nocover |
| | | name = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | |
| | | def make_bad_name(): |
| | | cls.make_property(lambda x: 1, name=name, reify=True) |
| | | |
| | | self.assertRaises(ConfigurationError, make_bad_name) |
| | | |
| | | def test_add_property(self): |
| | | helper = self._makeOne() |
| | | helper.add_property(lambda obj: obj.bar, name='x', reify=True) |
| | | helper.add_property(lambda obj: obj.bar, name='y') |
| | | self.assertEqual(len(helper.properties), 2) |
| | | foo = Dummy() |
| | | helper.apply(foo) |
| | | foo.bar = 1 |
| | | self.assertEqual(foo.x, 1) |
| | | self.assertEqual(foo.y, 1) |
| | | foo.bar = 2 |
| | | self.assertEqual(foo.x, 1) |
| | | self.assertEqual(foo.y, 2) |
| | | |
| | | def test_apply_multiple_times(self): |
| | | helper = self._makeOne() |
| | | helper.add_property(lambda obj: 1, name='x') |
| | | foo, bar = Dummy(), Dummy() |
| | | helper.apply(foo) |
| | | self.assertEqual(foo.x, 1) |
| | | helper.add_property(lambda obj: 2, name='x') |
| | | helper.apply(bar) |
| | | self.assertEqual(foo.x, 1) |
| | | self.assertEqual(bar.x, 2) |
| | | |
| | | class Test_InstancePropertyMixin(unittest.TestCase): |
| | | def _makeOne(self): |
| | | cls = self._getTargetClass() |
| | |
| | | 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__make_property_unicode(self): |
| | | from pyramid.compat import text_ |
| | | from pyramid.exceptions import ConfigurationError |
| | | |
| | | cls = self._getTargetClass() |
| | | if PY3: # pragma: nocover |
| | | name = b'La Pe\xc3\xb1a' |
| | | else: # pragma: nocover |
| | | name = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | |
| | | def make_bad_name(): |
| | | cls._make_property(lambda x: 1, name=name, reify=True) |
| | | |
| | | self.assertRaises(ConfigurationError, make_bad_name) |
| | | |
| | | 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): |
| | |
| | | self.assertEqual(self._callFUT(('a', 'b')), "('a', 'b')") |
| | | |
| | | def test_set(self): |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | self.assertEqual(self._callFUT(set(['a'])), "{'a'}") |
| | | else: # pragma: no cover |
| | | else: |
| | | self.assertEqual(self._callFUT(set(['a'])), "set(['a'])") |
| | | |
| | | def test_list(self): |
| | |
| | | else: # pragma: nocover |
| | | name = text_(b'hello world', 'utf-8') |
| | | |
| | | self.assertEquals(get_callable_name(name), 'hello world') |
| | | self.assertEqual(get_callable_name(name), 'hello world') |
| | | |
| | | def test_invalid_ascii(self): |
| | | from pyramid.util import get_callable_name |
| | |
| | | """ |
| | | |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | # special-case on Python 2 for speed? unchecked |
| | | def quote_path_segment(segment, safe=''): |
| | | """ %s """ % quote_path_segment_doc |
| | |
| | | def generator(dict): |
| | | newdict = {} |
| | | for k, v in dict.items(): |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | if v.__class__ is binary_type: |
| | | # url_quote below needs a native string, not bytes on Py3 |
| | | v = v.decode('utf-8') |
| | |
| | | ) |
| | | |
| | | from pyramid.compat import ( |
| | | iteritems_, |
| | | is_nonstr_iter, |
| | | integer_types, |
| | | string_types, |
| | |
| | | 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) |
| | | |
| | | _marker = object() |
| | | |
| | | 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. |
| | | |
| | | class InstancePropertyHelper(object): |
| | | """A helper object for assigning properties and descriptors to instances. |
| | | It is not normally possible to do this because descriptors must be |
| | | defined on the class itself. |
| | | |
| | | This class is optimized for adding multiple properties at once to an |
| | | instance. This is done by calling :meth:`.add_property` once |
| | | per-property and then invoking :meth:`.apply` on target objects. |
| | | |
| | | """ |
| | | def __init__(self): |
| | | self.properties = {} |
| | | |
| | | @classmethod |
| | | def _make_property(cls, callable, name=None, reify=False): |
| | | 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. |
| | |
| | | |
| | | 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`. |
| | | |
| | | ``properties`` is a sequence of two-tuples *or* a data structure |
| | | with an ``.items()`` method which returns a sequence of two-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. |
| | | @classmethod |
| | | def apply_properties(cls, target, properties): |
| | | """Accept a list or dict of ``properties`` generated from |
| | | :meth:`.make_property` and apply them to a ``target`` object. |
| | | """ |
| | | attrs = dict(properties) |
| | | |
| | | if attrs: |
| | | parent = self.__class__ |
| | | cls = type(parent.__name__, (parent, object), attrs) |
| | | parent = target.__class__ |
| | | newcls = type(parent.__name__, (parent, object), attrs) |
| | | # We assign __provides__, __implemented__ and __providedBy__ below |
| | | # to prevent a memory leak that results from from the usage of this |
| | | # instance's eventual use in an adapter lookup. Adapter lookup |
| | |
| | | # attached to it |
| | | val = getattr(parent, name, _marker) |
| | | if val is not _marker: |
| | | setattr(cls, name, val) |
| | | self.__class__ = cls |
| | | setattr(newcls, name, val) |
| | | target.__class__ = newcls |
| | | |
| | | def _set_extensions(self, extensions): |
| | | for name, fn in iteritems_(extensions.methods): |
| | | method = fn.__get__(self, self.__class__) |
| | | setattr(self, name, method) |
| | | @classmethod |
| | | def set_property(cls, target, callable, name=None, reify=False): |
| | | """A helper method to apply a single property to an instance.""" |
| | | prop = cls.make_property(callable, name=name, reify=reify) |
| | | cls.apply_properties(target, [prop]) |
| | | |
| | | self._set_properties(extensions.descriptors) |
| | | def add_property(self, callable, name=None, reify=False): |
| | | """Add a new property configuration. |
| | | |
| | | This should be used in combination with :meth:`.apply` as a |
| | | more efficient version of :meth:`.set_property`. |
| | | """ |
| | | name, fn = self.make_property(callable, name=name, reify=reify) |
| | | self.properties[name] = fn |
| | | |
| | | def apply(self, target): |
| | | """ Apply all configured properties to the ``target`` instance.""" |
| | | if self.properties: |
| | | self.apply_properties(target, self.properties) |
| | | |
| | | 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. |
| | | """ |
| | | |
| | | def set_property(self, callable, name=None, reify=False): |
| | | """ Add a callable or a property descriptor to the instance. |
| | |
| | | >>> foo.y # notice y keeps the original value |
| | | 1 |
| | | """ |
| | | prop = self._make_property(callable, name=name, reify=reify) |
| | | self._set_properties([prop]) |
| | | InstancePropertyHelper.set_property( |
| | | self, callable, name=name, reify=reify) |
| | | |
| | | class WeakOrderedSet(object): |
| | | """ Maintain a set of items. |
| | |
| | | if isinstance(object, (bool, float, type(None))): |
| | | return text_(str(object)) |
| | | if isinstance(object, set): |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | return shortrepr(object, '}') |
| | | else: |
| | | return shortrepr(object, ')') |
| | |
| | | match=^test |
| | | where=pyramid |
| | | nocapture=1 |
| | | cover-package=pyramid |
| | | cover-erase=1 |
| | | cover-min-percentage=100 |
| | | |
| | | [aliases] |
| | | dev = develop easy_install pyramid[testing] |
| | |
| | | [tox] |
| | | envlist = |
| | | py26,py27,py32,py33,py34,pypy,pypy3,cover |
| | | envlist = |
| | | py26,py27,py32,py33,py34,pypy,pypy3,pep8, |
| | | {py2,py3}-docs, |
| | | {py2,py3}-cover,coverage |
| | | |
| | | [testenv] |
| | | commands = |
| | | python setup.py -q dev |
| | | python setup.py -q test -q |
| | | |
| | | [testenv:cover] |
| | | # Most of these are defaults but if you specify any you can't fall back |
| | | # to defaults for others. |
| | | basepython = |
| | | python2.6 |
| | | commands = |
| | | python setup.py -q dev |
| | | nosetests --with-xunit --with-xcoverage |
| | | deps = |
| | | nosexcover |
| | | py26: python2.6 |
| | | py27: python2.7 |
| | | py32: python3.2 |
| | | py33: python3.3 |
| | | py34: python3.4 |
| | | pypy: pypy |
| | | pypy3: pypy3 |
| | | py2: python2.7 |
| | | py3: python3.4 |
| | | |
| | | commands = |
| | | pip install pyramid[testing] |
| | | nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:} |
| | | |
| | | [testenv:pep8] |
| | | basepython = python3.4 |
| | | commands = |
| | | flake8 pyramid/ |
| | | deps = |
| | |
| | | # 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. |
| | | [testenv:py2-cover] |
| | | commands = |
| | | pip install pyramid[testing] |
| | | coverage run --source=pyramid {envbindir}/nosetests |
| | | coverage xml -o coverage-py2.xml |
| | | setenv = |
| | | COVERAGE_FILE=.coverage.py2 |
| | | |
| | | [testenv:py3-cover] |
| | | commands = |
| | | pip install pyramid[testing] |
| | | coverage run --source=pyramid {envbindir}/nosetests |
| | | coverage xml -o coverage-py3.xml |
| | | setenv = |
| | | COVERAGE_FILE=.coverage.py3 |
| | | |
| | | [testenv:py2-docs] |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs html |
| | | |
| | | [testenv:py3-docs] |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs html |
| | | |
| | | [testenv:coverage] |
| | | basepython = python3.4 |
| | | commands = |
| | | coverage erase |
| | | coverage combine |
| | | coverage xml |
| | | coverage report --show-missing --fail-under=100 |
| | | deps = |
| | | coverage |
| | | setenv = |
| | | COVERAGE_FILE=.coverage |